diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /security/sandbox/chromium/base/threading | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
20 files changed, 3816 insertions, 0 deletions
diff --git a/security/sandbox/chromium/base/threading/platform_thread.cc b/security/sandbox/chromium/base/threading/platform_thread.cc new file mode 100644 index 0000000000..ae0a4499e7 --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread.cc @@ -0,0 +1,51 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/platform_thread.h" + +#include <atomic> +#include <memory> + +#include "base/feature_list.h" + +namespace base { + +namespace { + +// Whether thread priorities should be used. When disabled, +// PlatformThread::SetCurrentThreadPriority() no-ops. +const Feature kThreadPrioritiesFeature{"ThreadPriorities", + FEATURE_ENABLED_BY_DEFAULT}; + +// Whether thread priorities should be used. +// +// PlatformThread::SetCurrentThreadPriority() doesn't query the state of the +// feature directly because FeatureList initialization is not always +// synchronized with PlatformThread::SetCurrentThreadPriority(). +std::atomic<bool> g_use_thread_priorities(true); + +} // namespace + +// static +void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) { + if (g_use_thread_priorities.load()) + SetCurrentThreadPriorityImpl(priority); +} + +namespace internal { + +void InitializeThreadPrioritiesFeature() { + // A DCHECK is triggered on FeatureList initialization if the state of a + // feature has been checked before. To avoid triggering this DCHECK in unit + // tests that call this before initializing the FeatureList, only check the + // state of the feature if the FeatureList is initialized. + if (FeatureList::GetInstance() && + !FeatureList::IsEnabled(kThreadPrioritiesFeature)) { + g_use_thread_priorities.store(false); + } +} + +} // namespace internal + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/platform_thread.h b/security/sandbox/chromium/base/threading/platform_thread.h new file mode 100644 index 0000000000..aa5b9526c1 --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread.h @@ -0,0 +1,259 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: You should *NOT* be using this class directly. PlatformThread is +// the low-level platform-specific abstraction to the OS's threading interface. +// You should instead be using a message-loop driven Thread, see thread.h. + +#ifndef BASE_THREADING_PLATFORM_THREAD_H_ +#define BASE_THREADING_PLATFORM_THREAD_H_ + +#include <stddef.h> + +#include "base/base_export.h" +#include "base/macros.h" +#include "base/time/time.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/win/windows_types.h" +#elif defined(OS_FUCHSIA) +#include <zircon/types.h> +#elif defined(OS_MACOSX) +#include <mach/mach_types.h> +#elif defined(OS_POSIX) +#include <pthread.h> +#include <unistd.h> +#endif + +namespace base { + +// Used for logging. Always an integer value. +#if defined(OS_WIN) +typedef DWORD PlatformThreadId; +#elif defined(OS_FUCHSIA) +typedef zx_handle_t PlatformThreadId; +#elif defined(OS_MACOSX) +typedef mach_port_t PlatformThreadId; +#elif defined(OS_POSIX) +typedef pid_t PlatformThreadId; +#endif + +// Used for thread checking and debugging. +// Meant to be as fast as possible. +// These are produced by PlatformThread::CurrentRef(), and used to later +// check if we are on the same thread or not by using ==. These are safe +// to copy between threads, but can't be copied to another process as they +// have no meaning there. Also, the internal identifier can be re-used +// after a thread dies, so a PlatformThreadRef cannot be reliably used +// to distinguish a new thread from an old, dead thread. +class PlatformThreadRef { + public: +#if defined(OS_WIN) + typedef DWORD RefType; +#else // OS_POSIX + typedef pthread_t RefType; +#endif + constexpr PlatformThreadRef() : id_(0) {} + + explicit constexpr PlatformThreadRef(RefType id) : id_(id) {} + + bool operator==(PlatformThreadRef other) const { + return id_ == other.id_; + } + + bool operator!=(PlatformThreadRef other) const { return id_ != other.id_; } + + bool is_null() const { + return id_ == 0; + } + private: + RefType id_; +}; + +// Used to operate on threads. +class PlatformThreadHandle { + public: +#if defined(OS_WIN) + typedef void* Handle; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + typedef pthread_t Handle; +#endif + + constexpr PlatformThreadHandle() : handle_(0) {} + + explicit constexpr PlatformThreadHandle(Handle handle) : handle_(handle) {} + + bool is_equal(const PlatformThreadHandle& other) const { + return handle_ == other.handle_; + } + + bool is_null() const { + return !handle_; + } + + Handle platform_handle() const { + return handle_; + } + + private: + Handle handle_; +}; + +const PlatformThreadId kInvalidThreadId(0); + +// Valid values for priority of Thread::Options and SimpleThread::Options, and +// SetCurrentThreadPriority(), listed in increasing order of importance. +enum class ThreadPriority : int { + // Suitable for threads that shouldn't disrupt high priority work. + BACKGROUND, + // Default priority level. + NORMAL, + // Suitable for threads which generate data for the display (at ~60Hz). + DISPLAY, + // Suitable for low-latency, glitch-resistant audio. + REALTIME_AUDIO, +}; + +// A namespace for low-level thread functions. +class BASE_EXPORT PlatformThread { + public: + // Implement this interface to run code on a background thread. Your + // ThreadMain method will be called on the newly created thread. + class BASE_EXPORT Delegate { + public: + virtual void ThreadMain() = 0; + + protected: + virtual ~Delegate() = default; + }; + + // Gets the current thread id, which may be useful for logging purposes. + static PlatformThreadId CurrentId(); + + // Gets the current thread reference, which can be used to check if + // we're on the right thread quickly. + static PlatformThreadRef CurrentRef(); + + // Get the handle representing the current thread. On Windows, this is a + // pseudo handle constant which will always represent the thread using it and + // hence should not be shared with other threads nor be used to differentiate + // the current thread from another. + static PlatformThreadHandle CurrentHandle(); + + // Yield the current thread so another thread can be scheduled. + static void YieldCurrentThread(); + + // Sleeps for the specified duration (real-time; ignores time overrides). + // Note: The sleep duration may be in base::Time or base::TimeTicks, depending + // on platform. If you're looking to use this in unit tests testing delayed + // tasks, this will be unreliable - instead, use + // base::test::TaskEnvironment with MOCK_TIME mode. + static void Sleep(base::TimeDelta duration); + + // Sets the thread name visible to debuggers/tools. This will try to + // initialize the context for current thread unless it's a WorkerThread. + static void SetName(const std::string& name); + + // Gets the thread name, if previously set by SetName. + static const char* GetName(); + + // Creates a new thread. The |stack_size| parameter can be 0 to indicate + // that the default stack size should be used. Upon success, + // |*thread_handle| will be assigned a handle to the newly created thread, + // and |delegate|'s ThreadMain method will be executed on the newly created + // thread. + // NOTE: When you are done with the thread handle, you must call Join to + // release system resources associated with the thread. You must ensure that + // the Delegate object outlives the thread. + static bool Create(size_t stack_size, + Delegate* delegate, + PlatformThreadHandle* thread_handle) { + return CreateWithPriority(stack_size, delegate, thread_handle, + ThreadPriority::NORMAL); + } + + // CreateWithPriority() does the same thing as Create() except the priority of + // the thread is set based on |priority|. + static bool CreateWithPriority(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle, + ThreadPriority priority); + + // CreateNonJoinable() does the same thing as Create() except the thread + // cannot be Join()'d. Therefore, it also does not output a + // PlatformThreadHandle. + static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); + + // CreateNonJoinableWithPriority() does the same thing as CreateNonJoinable() + // except the priority of the thread is set based on |priority|. + static bool CreateNonJoinableWithPriority(size_t stack_size, + Delegate* delegate, + ThreadPriority priority); + + // Joins with a thread created via the Create function. This function blocks + // the caller until the designated thread exits. This will invalidate + // |thread_handle|. + static void Join(PlatformThreadHandle thread_handle); + + // Detaches and releases the thread handle. The thread is no longer joinable + // and |thread_handle| is invalidated after this call. + static void Detach(PlatformThreadHandle thread_handle); + + // Returns true if SetCurrentThreadPriority() should be able to increase the + // priority of a thread to |priority|. + static bool CanIncreaseThreadPriority(ThreadPriority priority); + + // Toggles the current thread's priority at runtime. + // + // A thread may not be able to raise its priority back up after lowering it if + // the process does not have a proper permission, e.g. CAP_SYS_NICE on Linux. + // A thread may not be able to lower its priority back down after raising it + // to REALTIME_AUDIO. + // + // This function must not be called from the main thread on Mac. This is to + // avoid performance regressions (https://crbug.com/601270). + // + // Since changing other threads' priority is not permitted in favor of + // security, this interface is restricted to change only the current thread + // priority (https://crbug.com/399473). + static void SetCurrentThreadPriority(ThreadPriority priority); + + static ThreadPriority GetCurrentThreadPriority(); + +#if defined(OS_LINUX) + // Toggles a specific thread's priority at runtime. This can be used to + // change the priority of a thread in a different process and will fail + // if the calling process does not have proper permissions. The + // SetCurrentThreadPriority() function above is preferred in favor of + // security but on platforms where sandboxed processes are not allowed to + // change priority this function exists to allow a non-sandboxed process + // to change the priority of sandboxed threads for improved performance. + // Warning: Don't use this for a main thread because that will change the + // whole thread group's (i.e. process) priority. + static void SetThreadPriority(PlatformThreadId thread_id, + ThreadPriority priority); +#endif + + // Returns the default thread stack size set by chrome. If we do not + // explicitly set default size then returns 0. + static size_t GetDefaultThreadStackSize(); + + private: + static void SetCurrentThreadPriorityImpl(ThreadPriority priority); + + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); +}; + +namespace internal { + +// Initializes the "ThreadPriorities" feature. The feature state is only taken +// into account after this initialization. This initialization must be +// synchronized with calls to PlatformThread::SetCurrentThreadPriority(). +void InitializeThreadPrioritiesFeature(); + +} // namespace internal + +} // namespace base + +#endif // BASE_THREADING_PLATFORM_THREAD_H_ diff --git a/security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc new file mode 100644 index 0000000000..378a24d0d1 --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc @@ -0,0 +1,39 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/platform_thread_internal_posix.h" + +#include "base/containers/adapters.h" +#include "base/logging.h" + +namespace base { + +namespace internal { + +int ThreadPriorityToNiceValue(ThreadPriority priority) { + for (const auto& pair : kThreadPriorityToNiceValueMap) { + if (pair.priority == priority) + return pair.nice_value; + } + NOTREACHED() << "Unknown ThreadPriority"; + return 0; +} + +ThreadPriority NiceValueToThreadPriority(int nice_value) { + // Try to find a priority that best describes |nice_value|. If there isn't + // an exact match, this method returns the closest priority whose nice value + // is higher (lower priority) than |nice_value|. + for (const auto& pair : Reversed(kThreadPriorityToNiceValueMap)) { + if (pair.nice_value >= nice_value) + return pair.priority; + } + + // Reaching here means |nice_value| is more than any of the defined + // priorities. The lowest priority is suitable in this case. + return ThreadPriority::BACKGROUND; +} + +} // namespace internal + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/platform_thread_internal_posix.h b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.h new file mode 100644 index 0000000000..d248fa9bfd --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.h @@ -0,0 +1,62 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ +#define BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ + +#include "base/base_export.h" +#include "base/optional.h" +#include "base/threading/platform_thread.h" +#include "build/build_config.h" + +namespace base { + +namespace internal { + +struct ThreadPriorityToNiceValuePair { + ThreadPriority priority; + int nice_value; +}; +// The elements must be listed in the order of increasing priority (lowest +// priority first), that is, in the order of decreasing nice values (highest +// nice value first). +BASE_EXPORT extern +const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4]; + +// Returns the nice value matching |priority| based on the platform-specific +// implementation of kThreadPriorityToNiceValueMap. +int ThreadPriorityToNiceValue(ThreadPriority priority); + +// Returns the ThreadPrioirty matching |nice_value| based on the platform- +// specific implementation of kThreadPriorityToNiceValueMap. +BASE_EXPORT ThreadPriority NiceValueToThreadPriority(int nice_value); + +// If non-nullopt, this return value will be used as the platform-specific +// result of CanIncreaseThreadPriority(). +Optional<bool> CanIncreaseCurrentThreadPriorityForPlatform( + ThreadPriority priority); + +// Allows platform specific tweaks to the generic POSIX solution for +// SetCurrentThreadPriority(). Returns true if the platform-specific +// implementation handled this |priority| change, false if the generic +// implementation should instead proceed. +bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority); + +// If non-null, this return value will be used as the platform-specific result +// of CanIncreaseThreadPriority(). +Optional<ThreadPriority> GetCurrentThreadPriorityForPlatform(); + +#if defined(OS_LINUX) +// Current thread id is cached in thread local storage for performance reasons. +// In some rare cases it's important to clear that cache explicitly (e.g. after +// going through clone() syscall which does not call pthread_atfork() +// handlers). +BASE_EXPORT void ClearTidCache(); +#endif // defined(OS_LINUX) + +} // namespace internal + +} // namespace base + +#endif // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ diff --git a/security/sandbox/chromium/base/threading/platform_thread_posix.cc b/security/sandbox/chromium/base/threading/platform_thread_posix.cc new file mode 100644 index 0000000000..335848e329 --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread_posix.cc @@ -0,0 +1,361 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/platform_thread.h" + +#include <errno.h> +#include <pthread.h> +#include <sched.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <memory> + +#include "base/debug/activity_tracker.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/threading/platform_thread_internal_posix.h" +#include "base/threading/scoped_blocking_call.h" +#include "base/threading/thread_id_name_manager.h" +#include "build/build_config.h" + +#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) && !defined(OS_NACL) +#include "base/posix/can_lower_nice_to.h" +#endif + +#if defined(OS_LINUX) +#include <sys/syscall.h> +#endif + +#if defined(OS_FUCHSIA) +#include <zircon/process.h> +#else +#include <sys/resource.h> +#endif + +namespace base { + +void InitThreading(); +void TerminateOnThread(); +size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes); + +namespace { + +struct ThreadParams { + ThreadParams() + : delegate(nullptr), joinable(false), priority(ThreadPriority::NORMAL) {} + + PlatformThread::Delegate* delegate; + bool joinable; + ThreadPriority priority; +}; + +void* ThreadFunc(void* params) { + PlatformThread::Delegate* delegate = nullptr; + + { + std::unique_ptr<ThreadParams> thread_params( + static_cast<ThreadParams*>(params)); + + delegate = thread_params->delegate; + if (!thread_params->joinable) + base::ThreadRestrictions::SetSingletonAllowed(false); + +#if !defined(OS_NACL) + // Threads on linux/android may inherit their priority from the thread + // where they were created. This explicitly sets the priority of all new + // threads. + PlatformThread::SetCurrentThreadPriority(thread_params->priority); +#endif + } + + ThreadIdNameManager::GetInstance()->RegisterThread( + PlatformThread::CurrentHandle().platform_handle(), + PlatformThread::CurrentId()); + + delegate->ThreadMain(); + + ThreadIdNameManager::GetInstance()->RemoveName( + PlatformThread::CurrentHandle().platform_handle(), + PlatformThread::CurrentId()); + + base::TerminateOnThread(); + return nullptr; +} + +bool CreateThread(size_t stack_size, + bool joinable, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* thread_handle, + ThreadPriority priority) { + DCHECK(thread_handle); + base::InitThreading(); + + pthread_attr_t attributes; + pthread_attr_init(&attributes); + + // Pthreads are joinable by default, so only specify the detached + // attribute if the thread should be non-joinable. + if (!joinable) + pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); + + // Get a better default if available. + if (stack_size == 0) + stack_size = base::GetDefaultThreadStackSize(attributes); + + if (stack_size > 0) + pthread_attr_setstacksize(&attributes, stack_size); + + std::unique_ptr<ThreadParams> params(new ThreadParams); + params->delegate = delegate; + params->joinable = joinable; + params->priority = priority; + + pthread_t handle; + int err = pthread_create(&handle, &attributes, ThreadFunc, params.get()); + bool success = !err; + if (success) { + // ThreadParams should be deleted on the created thread after used. + ignore_result(params.release()); + } else { + // Value of |handle| is undefined if pthread_create fails. + handle = 0; + errno = err; + PLOG(ERROR) << "pthread_create"; + } + *thread_handle = PlatformThreadHandle(handle); + + pthread_attr_destroy(&attributes); + + return success; +} + +#if defined(OS_LINUX) + +// Store the thread ids in local storage since calling the SWI can +// expensive and PlatformThread::CurrentId is used liberally. Clear +// the stored value after a fork() because forking changes the thread +// id. Forking without going through fork() (e.g. clone()) is not +// supported, but there is no known usage. Using thread_local is +// fine here (despite being banned) since it is going to be allowed +// but is blocked on a clang bug for Mac (https://crbug.com/829078) +// and we can't use ThreadLocalStorage because of re-entrancy due to +// CHECK/DCHECKs. +thread_local pid_t g_thread_id = -1; + +class InitAtFork { + public: + InitAtFork() { pthread_atfork(nullptr, nullptr, internal::ClearTidCache); } +}; + +#endif // defined(OS_LINUX) + +} // namespace + +#if defined(OS_LINUX) + +namespace internal { + +void ClearTidCache() { + g_thread_id = -1; +} + +} // namespace internal + +#endif // defined(OS_LINUX) + +// static +PlatformThreadId PlatformThread::CurrentId() { + // Pthreads doesn't have the concept of a thread ID, so we have to reach down + // into the kernel. +#if defined(OS_MACOSX) + return pthread_mach_thread_np(pthread_self()); +#elif defined(OS_LINUX) + static NoDestructor<InitAtFork> init_at_fork; + if (g_thread_id == -1) { + g_thread_id = syscall(__NR_gettid); + } else { + DCHECK_EQ(g_thread_id, syscall(__NR_gettid)) + << "Thread id stored in TLS is different from thread id returned by " + "the system. It is likely that the process was forked without going " + "through fork()."; + } + return g_thread_id; +#elif defined(OS_ANDROID) + return gettid(); +#elif defined(OS_FUCHSIA) + return zx_thread_self(); +#elif defined(OS_SOLARIS) || defined(OS_QNX) + return pthread_self(); +#elif defined(OS_NACL) && defined(__GLIBC__) + return pthread_self(); +#elif defined(OS_NACL) && !defined(__GLIBC__) + // Pointers are 32-bits in NaCl. + return reinterpret_cast<int32_t>(pthread_self()); +#elif defined(OS_POSIX) && defined(OS_AIX) + return pthread_self(); +#elif defined(OS_POSIX) && !defined(OS_AIX) + return reinterpret_cast<int64_t>(pthread_self()); +#endif +} + +// static +PlatformThreadRef PlatformThread::CurrentRef() { + return PlatformThreadRef(pthread_self()); +} + +// static +PlatformThreadHandle PlatformThread::CurrentHandle() { + return PlatformThreadHandle(pthread_self()); +} + +// static +void PlatformThread::YieldCurrentThread() { + sched_yield(); +} + +// static +void PlatformThread::Sleep(TimeDelta duration) { + struct timespec sleep_time, remaining; + + // Break the duration into seconds and nanoseconds. + // NOTE: TimeDelta's microseconds are int64s while timespec's + // nanoseconds are longs, so this unpacking must prevent overflow. + sleep_time.tv_sec = duration.InSeconds(); + duration -= TimeDelta::FromSeconds(sleep_time.tv_sec); + sleep_time.tv_nsec = duration.InMicroseconds() * 1000; // nanoseconds + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) + sleep_time = remaining; +} + +// static +const char* PlatformThread::GetName() { + return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); +} + +// static +bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle, + ThreadPriority priority) { + return CreateThread(stack_size, true /* joinable thread */, delegate, + thread_handle, priority); +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + return CreateNonJoinableWithPriority(stack_size, delegate, + ThreadPriority::NORMAL); +} + +// static +bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size, + Delegate* delegate, + ThreadPriority priority) { + PlatformThreadHandle unused; + + bool result = CreateThread(stack_size, false /* non-joinable thread */, + delegate, &unused, priority); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + // Record the event that this thread is blocking upon (for hang diagnosis). + base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle); + + // Joining another thread may block the current thread for a long time, since + // the thread referred to by |thread_handle| may still be running long-lived / + // blocking tasks. + base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( + FROM_HERE, base::BlockingType::MAY_BLOCK); + CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), nullptr)); +} + +// static +void PlatformThread::Detach(PlatformThreadHandle thread_handle) { + CHECK_EQ(0, pthread_detach(thread_handle.platform_handle())); +} + +// Mac and Fuchsia have their own Set/GetCurrentThreadPriority() +// implementations. +#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) + +// static +bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) { +#if defined(OS_NACL) + return false; +#else + auto platform_specific_ability = + internal::CanIncreaseCurrentThreadPriorityForPlatform(priority); + if (platform_specific_ability) + return platform_specific_ability.value(); + + return internal::CanLowerNiceTo( + internal::ThreadPriorityToNiceValue(priority)); +#endif // defined(OS_NACL) +} + +// static +void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) { +#if defined(OS_NACL) + NOTIMPLEMENTED(); +#else + if (internal::SetCurrentThreadPriorityForPlatform(priority)) + return; + + // setpriority(2) should change the whole thread group's (i.e. process) + // priority. However, as stated in the bugs section of + // http://man7.org/linux/man-pages/man2/getpriority.2.html: "under the current + // Linux/NPTL implementation of POSIX threads, the nice value is a per-thread + // attribute". Also, 0 is prefered to the current thread id since it is + // equivalent but makes sandboxing easier (https://crbug.com/399473). + const int nice_setting = internal::ThreadPriorityToNiceValue(priority); + if (setpriority(PRIO_PROCESS, 0, nice_setting)) { + DVPLOG(1) << "Failed to set nice value of thread (" + << PlatformThread::CurrentId() << ") to " << nice_setting; + } +#endif // defined(OS_NACL) +} + +// static +ThreadPriority PlatformThread::GetCurrentThreadPriority() { +#if defined(OS_NACL) + NOTIMPLEMENTED(); + return ThreadPriority::NORMAL; +#else + // Mirrors SetCurrentThreadPriority()'s implementation. + auto platform_specific_priority = + internal::GetCurrentThreadPriorityForPlatform(); + if (platform_specific_priority) + return platform_specific_priority.value(); + + // Need to clear errno before calling getpriority(): + // http://man7.org/linux/man-pages/man2/getpriority.2.html + errno = 0; + int nice_value = getpriority(PRIO_PROCESS, 0); + if (errno != 0) { + DVPLOG(1) << "Failed to get nice value of thread (" + << PlatformThread::CurrentId() << ")"; + return ThreadPriority::NORMAL; + } + + return internal::NiceValueToThreadPriority(nice_value); +#endif // !defined(OS_NACL) +} + +#endif // !defined(OS_MACOSX) && !defined(OS_FUCHSIA) + +// static +size_t PlatformThread::GetDefaultThreadStackSize() { + pthread_attr_t attributes; + pthread_attr_init(&attributes); + return base::GetDefaultThreadStackSize(attributes); +} + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/platform_thread_win.cc b/security/sandbox/chromium/base/threading/platform_thread_win.cc new file mode 100644 index 0000000000..0bcf6db247 --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread_win.cc @@ -0,0 +1,463 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/platform_thread_win.h" + +#include <stddef.h> + +#include "base/debug/activity_tracker.h" +#include "base/debug/alias.h" +#include "base/debug/crash_logging.h" +#include "base/debug/profiler.h" +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "base/process/memory.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/scoped_blocking_call.h" +#include "base/threading/thread_id_name_manager.h" +#include "base/threading/thread_restrictions.h" +#include "base/time/time_override.h" +#include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" +#include "build/build_config.h" + +#include <windows.h> + +namespace base { + +namespace { + +// The most common value returned by ::GetThreadPriority() after background +// thread mode is enabled on Windows 7. +constexpr int kWin7BackgroundThreadModePriority = 4; + +// Value sometimes returned by ::GetThreadPriority() after thread priority is +// set to normal on Windows 7. +constexpr int kWin7NormalPriority = 3; + +// These values are sometimes returned by ::GetThreadPriority(). +constexpr int kWinNormalPriority1 = 5; +constexpr int kWinNormalPriority2 = 6; + +// The information on how to set the thread name comes from +// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx +const DWORD kVCThreadNameException = 0x406D1388; + +typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +// The SetThreadDescription API was brought in version 1607 of Windows 10. +typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, + PCWSTR lpThreadDescription); + +// This function has try handling, so it is separated out of its caller. +void SetNameInternal(PlatformThreadId thread_id, const char* name) { + //This function is only used for debugging purposes, as you can find by its caller +#ifndef __MINGW32__ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = thread_id; + info.dwFlags = 0; + + __try { + RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD), + reinterpret_cast<DWORD_PTR*>(&info)); + } __except(EXCEPTION_CONTINUE_EXECUTION) { + } +#endif +} + +struct ThreadParams { + PlatformThread::Delegate* delegate; + bool joinable; + ThreadPriority priority; +}; + +DWORD __stdcall ThreadFunc(void* params) { + ThreadParams* thread_params = static_cast<ThreadParams*>(params); + PlatformThread::Delegate* delegate = thread_params->delegate; + if (!thread_params->joinable) + base::ThreadRestrictions::SetSingletonAllowed(false); + + if (thread_params->priority != ThreadPriority::NORMAL) + PlatformThread::SetCurrentThreadPriority(thread_params->priority); + + // Retrieve a copy of the thread handle to use as the key in the + // thread name mapping. + PlatformThreadHandle::Handle platform_handle; + BOOL did_dup = DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &platform_handle, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + + win::ScopedHandle scoped_platform_handle; + + if (did_dup) { + scoped_platform_handle.Set(platform_handle); + ThreadIdNameManager::GetInstance()->RegisterThread( + scoped_platform_handle.Get(), + PlatformThread::CurrentId()); + } + + delete thread_params; + delegate->ThreadMain(); + + if (did_dup) { + ThreadIdNameManager::GetInstance()->RemoveName( + scoped_platform_handle.Get(), + PlatformThread::CurrentId()); + } + + return 0; +} + +// CreateThreadInternal() matches PlatformThread::CreateWithPriority(), except +// that |out_thread_handle| may be nullptr, in which case a non-joinable thread +// is created. +bool CreateThreadInternal(size_t stack_size, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* out_thread_handle, + ThreadPriority priority) { + unsigned int flags = 0; + if (stack_size > 0) { + flags = STACK_SIZE_PARAM_IS_A_RESERVATION; +#if defined(ARCH_CPU_32_BITS) + } else { + // The process stack size is increased to give spaces to |RendererMain| in + // |chrome/BUILD.gn|, but keep the default stack size of other threads to + // 1MB for the address space pressure. + flags = STACK_SIZE_PARAM_IS_A_RESERVATION; + stack_size = 1024 * 1024; +#endif + } + + ThreadParams* params = new ThreadParams; + params->delegate = delegate; + params->joinable = out_thread_handle != nullptr; + params->priority = priority; + + // Using CreateThread here vs _beginthreadex makes thread creation a bit + // faster and doesn't require the loader lock to be available. Our code will + // have to work running on CreateThread() threads anyway, since we run code on + // the Windows thread pool, etc. For some background on the difference: + // http://www.microsoft.com/msj/1099/win32/win321099.aspx + void* thread_handle = + ::CreateThread(nullptr, stack_size, ThreadFunc, params, flags, nullptr); + + if (!thread_handle) { + DWORD last_error = ::GetLastError(); + + switch (last_error) { + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + case ERROR_COMMITMENT_LIMIT: + TerminateBecauseOutOfMemory(stack_size); + break; + + default: + static auto* last_error_crash_key = debug::AllocateCrashKeyString( + "create_thread_last_error", debug::CrashKeySize::Size32); + debug::SetCrashKeyString(last_error_crash_key, + base::NumberToString(last_error)); + break; + } + + delete params; + return false; + } + + if (out_thread_handle) + *out_thread_handle = PlatformThreadHandle(thread_handle); + else + CloseHandle(thread_handle); + return true; +} + +} // namespace + +namespace internal { + +void AssertMemoryPriority(HANDLE thread, int memory_priority) { +#if DCHECK_IS_ON() + static const auto get_thread_information_fn = + reinterpret_cast<decltype(&::GetThreadInformation)>(::GetProcAddress( + ::GetModuleHandle(L"Kernel32.dll"), "GetThreadInformation")); + + if (!get_thread_information_fn) { + DCHECK_EQ(win::GetVersion(), win::Version::WIN7); + return; + } + + MEMORY_PRIORITY_INFORMATION memory_priority_information = {}; + DCHECK(get_thread_information_fn(thread, ::ThreadMemoryPriority, + &memory_priority_information, + sizeof(memory_priority_information))); + + DCHECK_EQ(memory_priority, + static_cast<int>(memory_priority_information.MemoryPriority)); +#endif +} + +} // namespace internal + +// static +PlatformThreadId PlatformThread::CurrentId() { + return ::GetCurrentThreadId(); +} + +// static +PlatformThreadRef PlatformThread::CurrentRef() { + return PlatformThreadRef(::GetCurrentThreadId()); +} + +// static +PlatformThreadHandle PlatformThread::CurrentHandle() { + return PlatformThreadHandle(::GetCurrentThread()); +} + +// static +void PlatformThread::YieldCurrentThread() { + ::Sleep(0); +} + +// static +void PlatformThread::Sleep(TimeDelta duration) { + // When measured with a high resolution clock, Sleep() sometimes returns much + // too early. We may need to call it repeatedly to get the desired duration. + // PlatformThread::Sleep doesn't support mock-time, so this always uses + // real-time. + const TimeTicks end = subtle::TimeTicksNowIgnoringOverride() + duration; + for (TimeTicks now = subtle::TimeTicksNowIgnoringOverride(); now < end; + now = subtle::TimeTicksNowIgnoringOverride()) { + ::Sleep(static_cast<DWORD>((end - now).InMillisecondsRoundedUp())); + } +} + +// static +void PlatformThread::SetName(const std::string& name) { + ThreadIdNameManager::GetInstance()->SetName(name); + + // The SetThreadDescription API works even if no debugger is attached. + static auto set_thread_description_func = + reinterpret_cast<SetThreadDescription>(::GetProcAddress( + ::GetModuleHandle(L"Kernel32.dll"), "SetThreadDescription")); + if (set_thread_description_func) { + set_thread_description_func(::GetCurrentThread(), + base::UTF8ToWide(name).c_str()); + } + + // The debugger needs to be around to catch the name in the exception. If + // there isn't a debugger, we are just needlessly throwing an exception. + if (!::IsDebuggerPresent()) + return; + + SetNameInternal(CurrentId(), name.c_str()); +} + +// static +const char* PlatformThread::GetName() { + return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); +} + +// static +bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle, + ThreadPriority priority) { + DCHECK(thread_handle); + return CreateThreadInternal(stack_size, delegate, thread_handle, priority); +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + return CreateNonJoinableWithPriority(stack_size, delegate, + ThreadPriority::NORMAL); +} + +// static +bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size, + Delegate* delegate, + ThreadPriority priority) { + return CreateThreadInternal(stack_size, delegate, nullptr /* non-joinable */, + priority); +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + DCHECK(thread_handle.platform_handle()); + + DWORD thread_id = 0; + thread_id = ::GetThreadId(thread_handle.platform_handle()); + DWORD last_error = 0; + if (!thread_id) + last_error = ::GetLastError(); + + // Record information about the exiting thread in case joining hangs. + base::debug::Alias(&thread_id); + base::debug::Alias(&last_error); + + // Record the event that this thread is blocking upon (for hang diagnosis). + base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle); + + base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( + FROM_HERE, base::BlockingType::MAY_BLOCK); + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + CHECK_EQ(WAIT_OBJECT_0, + WaitForSingleObject(thread_handle.platform_handle(), INFINITE)); + CloseHandle(thread_handle.platform_handle()); +} + +// static +void PlatformThread::Detach(PlatformThreadHandle thread_handle) { + CloseHandle(thread_handle.platform_handle()); +} + +// static +bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) { + return true; +} + +// static +void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) { + PlatformThreadHandle::Handle thread_handle = + PlatformThread::CurrentHandle().platform_handle(); + + if (priority != ThreadPriority::BACKGROUND) { + // Exit background mode if the new priority is not BACKGROUND. This is a + // no-op if not in background mode. + ::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END); + internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL); + } + + int desired_priority = THREAD_PRIORITY_ERROR_RETURN; + switch (priority) { + case ThreadPriority::BACKGROUND: + // Using THREAD_MODE_BACKGROUND_BEGIN instead of THREAD_PRIORITY_LOWEST + // improves input latency and navigation time. See + // https://docs.google.com/document/d/16XrOwuwTwKWdgPbcKKajTmNqtB4Am8TgS9GjbzBYLc0 + // + // MSDN recommends THREAD_MODE_BACKGROUND_BEGIN for threads that perform + // background work, as it reduces disk and memory priority in addition to + // CPU priority. + desired_priority = THREAD_MODE_BACKGROUND_BEGIN; + break; + case ThreadPriority::NORMAL: + desired_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::DISPLAY: + desired_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case ThreadPriority::REALTIME_AUDIO: + desired_priority = THREAD_PRIORITY_TIME_CRITICAL; + break; + default: + NOTREACHED() << "Unknown priority."; + break; + } + DCHECK_NE(desired_priority, THREAD_PRIORITY_ERROR_RETURN); + +#if DCHECK_IS_ON() + const BOOL success = +#endif + ::SetThreadPriority(thread_handle, desired_priority); + DPLOG_IF(ERROR, !success) << "Failed to set thread priority to " + << desired_priority; + + if (priority == ThreadPriority::BACKGROUND) { + // In a background process, THREAD_MODE_BACKGROUND_BEGIN lowers the memory + // and I/O priorities but not the CPU priority (kernel bug?). Use + // THREAD_PRIORITY_LOWEST to also lower the CPU priority. + // https://crbug.com/901483 + if (GetCurrentThreadPriority() != ThreadPriority::BACKGROUND) { + ::SetThreadPriority(thread_handle, THREAD_PRIORITY_LOWEST); + // Make sure that using THREAD_PRIORITY_LOWEST didn't affect the memory + // priority set by THREAD_MODE_BACKGROUND_BEGIN. There is no practical + // way to verify the I/O priority. + internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW); + } + } + + DCHECK_EQ(GetCurrentThreadPriority(), priority); +} + +// static +ThreadPriority PlatformThread::GetCurrentThreadPriority() { + static_assert( + THREAD_PRIORITY_IDLE < 0, + "THREAD_PRIORITY_IDLE is >= 0 and will incorrectly cause errors."); + static_assert( + THREAD_PRIORITY_LOWEST < 0, + "THREAD_PRIORITY_LOWEST is >= 0 and will incorrectly cause errors."); + static_assert(THREAD_PRIORITY_BELOW_NORMAL < 0, + "THREAD_PRIORITY_BELOW_NORMAL is >= 0 and will incorrectly " + "cause errors."); + static_assert( + THREAD_PRIORITY_NORMAL == 0, + "The logic below assumes that THREAD_PRIORITY_NORMAL is zero. If it is " + "not, ThreadPriority::BACKGROUND may be incorrectly detected."); + static_assert(THREAD_PRIORITY_ABOVE_NORMAL >= 0, + "THREAD_PRIORITY_ABOVE_NORMAL is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + static_assert(THREAD_PRIORITY_HIGHEST >= 0, + "THREAD_PRIORITY_HIGHEST is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + static_assert(THREAD_PRIORITY_TIME_CRITICAL >= 0, + "THREAD_PRIORITY_TIME_CRITICAL is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + static_assert(THREAD_PRIORITY_ERROR_RETURN >= 0, + "THREAD_PRIORITY_ERROR_RETURN is < 0 and will incorrectly be " + "translated to ThreadPriority::BACKGROUND."); + + const int priority = + ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle()); + + // Negative values represent a background priority. We have observed -3, -4, + // -6 when THREAD_MODE_BACKGROUND_* is used. THREAD_PRIORITY_IDLE, + // THREAD_PRIORITY_LOWEST and THREAD_PRIORITY_BELOW_NORMAL are other possible + // negative values. + if (priority < THREAD_PRIORITY_NORMAL) + return ThreadPriority::BACKGROUND; + + switch (priority) { + case kWin7BackgroundThreadModePriority: + DCHECK_EQ(win::GetVersion(), win::Version::WIN7); + return ThreadPriority::BACKGROUND; + case kWin7NormalPriority: + DCHECK_EQ(win::GetVersion(), win::Version::WIN7); + FALLTHROUGH; + case THREAD_PRIORITY_NORMAL: + return ThreadPriority::NORMAL; + case kWinNormalPriority1: + FALLTHROUGH; + case kWinNormalPriority2: + return ThreadPriority::NORMAL; + case THREAD_PRIORITY_ABOVE_NORMAL: + case THREAD_PRIORITY_HIGHEST: + return ThreadPriority::DISPLAY; + case THREAD_PRIORITY_TIME_CRITICAL: + return ThreadPriority::REALTIME_AUDIO; + case THREAD_PRIORITY_ERROR_RETURN: + DPCHECK(false) << "::GetThreadPriority error"; + } + + NOTREACHED() << "::GetThreadPriority returned " << priority << "."; + return ThreadPriority::NORMAL; +} + +// static +size_t PlatformThread::GetDefaultThreadStackSize() { + return 0; +} + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/platform_thread_win.h b/security/sandbox/chromium/base/threading/platform_thread_win.h new file mode 100644 index 0000000000..3e833178e3 --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread_win.h @@ -0,0 +1,23 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_PLATFORM_THREAD_WIN_H_ +#define BASE_THREADING_PLATFORM_THREAD_WIN_H_ + +#include "base/threading/platform_thread.h" + +#include "base/base_export.h" + +namespace base { +namespace internal { + +// Assert that the memory priority of |thread| is |memory_priority|. No-op on +// Windows 7 because ::GetThreadInformation() is not available. Exposed for unit +// tests. +BASE_EXPORT void AssertMemoryPriority(HANDLE thread, int memory_priority); + +} // namespace internal +} // namespace base + +#endif // BASE_THREADING_PLATFORM_THREAD_WIN_H_ diff --git a/security/sandbox/chromium/base/threading/thread_checker_impl.h b/security/sandbox/chromium/base/threading/thread_checker_impl.h new file mode 100644 index 0000000000..b325db6ae8 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_checker_impl.h @@ -0,0 +1,74 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_THREAD_CHECKER_IMPL_H_ +#define BASE_THREADING_THREAD_CHECKER_IMPL_H_ + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/sequence_token.h" +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" +#include "base/threading/platform_thread.h" + +namespace base { + +// Real implementation of ThreadChecker, for use in debug mode, or for temporary +// use in release mode (e.g. to CHECK on a threading issue seen only in the +// wild). +// +// Note: You should almost always use the ThreadChecker class to get the right +// version for your build configuration. +// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in +// order to support thread_annotations.h. +class LOCKABLE BASE_EXPORT ThreadCheckerImpl { + public: + ThreadCheckerImpl(); + ~ThreadCheckerImpl(); + + // Allow move construct/assign. This must be called on |other|'s associated + // thread and assignment can only be made into a ThreadCheckerImpl which is + // detached or already associated with the current thread. This isn't + // thread-safe (|this| and |other| shouldn't be in use while this move is + // performed). If the assignment was legal, the resulting ThreadCheckerImpl + // will be bound to the current thread and |other| will be detached. + ThreadCheckerImpl(ThreadCheckerImpl&& other); + ThreadCheckerImpl& operator=(ThreadCheckerImpl&& other); + + bool CalledOnValidThread() const WARN_UNUSED_RESULT; + + // Changes the thread that is checked for in CalledOnValidThread. This may + // be useful when an object may be created on one thread and then used + // exclusively on another thread. + void DetachFromThread(); + + private: + void EnsureAssignedLockRequired() const EXCLUSIVE_LOCKS_REQUIRED(lock_); + + // Members are mutable so that CalledOnValidThread() can set them. + + // Synchronizes access to all members. + mutable base::Lock lock_; + + // Thread on which CalledOnValidThread() may return true. + mutable PlatformThreadRef thread_id_ GUARDED_BY(lock_); + + // TaskToken for which CalledOnValidThread() always returns true. This allows + // CalledOnValidThread() to return true when called multiple times from the + // same task, even if it's not running in a single-threaded context itself + // (allowing usage of ThreadChecker objects on the stack in the scope of one- + // off tasks). Note: CalledOnValidThread() may return true even if the current + // TaskToken is not equal to this. + mutable TaskToken task_token_ GUARDED_BY(lock_); + + // SequenceToken for which CalledOnValidThread() may return true. Used to + // ensure that CalledOnValidThread() doesn't return true for ThreadPool + // tasks that happen to run on the same thread but weren't posted to the same + // SingleThreadTaskRunner. + mutable SequenceToken sequence_token_ GUARDED_BY(lock_); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_CHECKER_IMPL_H_ diff --git a/security/sandbox/chromium/base/threading/thread_collision_warner.cc b/security/sandbox/chromium/base/threading/thread_collision_warner.cc new file mode 100644 index 0000000000..547e11ca66 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_collision_warner.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/thread_collision_warner.h" + +#include "base/logging.h" +#include "base/threading/platform_thread.h" + +namespace base { + +void DCheckAsserter::warn() { + NOTREACHED() << "Thread Collision"; +} + +static subtle::Atomic32 CurrentThread() { + const PlatformThreadId current_thread_id = PlatformThread::CurrentId(); + // We need to get the thread id into an atomic data type. This might be a + // truncating conversion, but any loss-of-information just increases the + // chance of a fault negative, not a false positive. + const subtle::Atomic32 atomic_thread_id = + static_cast<subtle::Atomic32>(current_thread_id); + + return atomic_thread_id; +} + +void ThreadCollisionWarner::EnterSelf() { + // If the active thread is 0 then I'll write the current thread ID + // if two or more threads arrive here only one will succeed to + // write on valid_thread_id_ the current thread ID. + subtle::Atomic32 current_thread_id = CurrentThread(); + + int previous_value = subtle::NoBarrier_CompareAndSwap(&valid_thread_id_, + 0, + current_thread_id); + if (previous_value != 0 && previous_value != current_thread_id) { + // gotcha! a thread is trying to use the same class and that is + // not current thread. + asserter_->warn(); + } + + subtle::NoBarrier_AtomicIncrement(&counter_, 1); +} + +void ThreadCollisionWarner::Enter() { + subtle::Atomic32 current_thread_id = CurrentThread(); + + if (subtle::NoBarrier_CompareAndSwap(&valid_thread_id_, + 0, + current_thread_id) != 0) { + // gotcha! another thread is trying to use the same class. + asserter_->warn(); + } + + subtle::NoBarrier_AtomicIncrement(&counter_, 1); +} + +void ThreadCollisionWarner::Leave() { + if (subtle::Barrier_AtomicIncrement(&counter_, -1) == 0) { + subtle::NoBarrier_Store(&valid_thread_id_, 0); + } +} + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/thread_collision_warner.h b/security/sandbox/chromium/base/threading/thread_collision_warner.h new file mode 100644 index 0000000000..7f7443b21d --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_collision_warner.h @@ -0,0 +1,252 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_THREAD_COLLISION_WARNER_H_ +#define BASE_THREADING_THREAD_COLLISION_WARNER_H_ + +#include <memory> + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/macros.h" + +// A helper class alongside macros to be used to verify assumptions about thread +// safety of a class. +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow. +// +// In this case the macro DFAKE_SCOPED_LOCK has to be +// used, it checks that if a thread is inside the push/pop then +// noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow, it calls a method to "protect" from +// a "protected" method +// +// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK +// has to be used, it checks that if a thread is inside the push/pop +// then noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// void push(int) { +// DFAKE_SCOPED_LOCK(push_pop_); +// ... +// } +// int pop() { +// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); +// bar(); +// ... +// } +// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation not usable even if clients are synchronized, +// so only one thread in the class life cycle can use the two members +// push/pop. +// +// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the +// specified +// critical section the first time a thread enters push or pop, from +// that time on only that thread is allowed to execute push or pop. +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Class that has to be contructed/destroyed on same thread, it has +// a "shareable" method (with external synchronization) and a not +// shareable method (even with external synchronization). +// +// In this case 3 Critical sections have to be defined +// +// class ExoticClass { +// public: +// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// +// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } +// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ... +// private: +// DFAKE_MUTEX(ctor_dtor_); +// DFAKE_MUTEX(shareable_section_); +// }; + + +#if !defined(NDEBUG) + +#define DFAKE_UNIQUE_VARIABLE_CONCAT(a, b) a##b +// CONCAT1 provides extra level of indirection so that __LINE__ macro expands. +#define DFAKE_UNIQUE_VARIABLE_CONCAT1(a, b) DFAKE_UNIQUE_VARIABLE_CONCAT(a, b) +#define DFAKE_UNIQUE_VARIABLE_NAME(a) DFAKE_UNIQUE_VARIABLE_CONCAT1(a, __LINE__) + +// Defines a class member that acts like a mutex. It is used only as a +// verification tool. +#define DFAKE_MUTEX(obj) \ + mutable base::ThreadCollisionWarner obj +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. +#define DFAKE_SCOPED_LOCK(obj) \ + base::ThreadCollisionWarner::ScopedCheck DFAKE_UNIQUE_VARIABLE_NAME( \ + s_check_)(&obj) +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ + base::ThreadCollisionWarner::ScopedRecursiveCheck \ + DFAKE_UNIQUE_VARIABLE_NAME(sr_check)(&obj) +// Asserts the code is always executed in the same thread. +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ + base::ThreadCollisionWarner::Check DFAKE_UNIQUE_VARIABLE_NAME(check_)(&obj) + +#else + +#define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj +#define DFAKE_SCOPED_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) + +#endif + +namespace base { + +// The class ThreadCollisionWarner uses an Asserter to notify the collision +// AsserterBase is the interfaces and DCheckAsserter is the default asserter +// used. During the unit tests is used another class that doesn't "DCHECK" +// in case of collision (check thread_collision_warner_unittests.cc) +struct BASE_EXPORT AsserterBase { + virtual ~AsserterBase() = default; + virtual void warn() = 0; +}; + +struct BASE_EXPORT DCheckAsserter : public AsserterBase { + ~DCheckAsserter() override = default; + void warn() override; +}; + +class BASE_EXPORT ThreadCollisionWarner { + public: + // The parameter asserter is there only for test purpose + explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) + : valid_thread_id_(0), + counter_(0), + asserter_(asserter) {} + + ~ThreadCollisionWarner() { + delete asserter_; + } + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK_THREAD_LOCKED + // it doesn't leave the critical section, as opposed to ScopedCheck, + // because the critical section being pinned is allowed to be used only + // from one thread + class BASE_EXPORT Check { + public: + explicit Check(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->EnterSelf(); + } + + ~Check() = default; + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(Check); + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK + class BASE_EXPORT ScopedCheck { + public: + explicit ScopedCheck(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->Enter(); + } + + ~ScopedCheck() { + warner_->Leave(); + } + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCheck); + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_RECURSIVE_LOCK + class BASE_EXPORT ScopedRecursiveCheck { + public: + explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->EnterSelf(); + } + + ~ScopedRecursiveCheck() { + warner_->Leave(); + } + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck); + }; + + private: + // This method stores the current thread identifier and does a DCHECK + // if a another thread has already done it, it is safe if same thread + // calls this multiple time (recursion allowed). + void EnterSelf(); + + // Same as EnterSelf but recursion is not allowed. + void Enter(); + + // Removes the thread_id stored in order to allow other threads to + // call EnterSelf or Enter. + void Leave(); + + // This stores the thread id that is inside the critical section, if the + // value is 0 then no thread is inside. + volatile subtle::Atomic32 valid_thread_id_; + + // Counter to trace how many time a critical section was "pinned" + // (when allowed) in order to unpin it when counter_ reaches 0. + volatile subtle::Atomic32 counter_; + + // Here only for class unit tests purpose, during the test I need to not + // DCHECK but notify the collision with something else. + AsserterBase* asserter_; + + DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_ diff --git a/security/sandbox/chromium/base/threading/thread_id_name_manager.cc b/security/sandbox/chromium/base/threading/thread_id_name_manager.cc new file mode 100644 index 0000000000..ba2f9b41cb --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_id_name_manager.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/thread_id_name_manager.h" + +#include <stdlib.h> +#include <string.h> + +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/no_destructor.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/threading/thread_local.h" +#include "base/trace_event/heap_profiler_allocation_context_tracker.h" + +namespace base { +namespace { + +static const char kDefaultName[] = ""; +static std::string* g_default_name; + +ThreadLocalStorage::Slot& GetThreadNameTLS() { + static base::NoDestructor<base::ThreadLocalStorage::Slot> thread_name_tls; + return *thread_name_tls; +} +} + +ThreadIdNameManager::Observer::~Observer() = default; + +ThreadIdNameManager::ThreadIdNameManager() + : main_process_name_(nullptr), main_process_id_(kInvalidThreadId) { + g_default_name = new std::string(kDefaultName); + + AutoLock locked(lock_); + name_to_interned_name_[kDefaultName] = g_default_name; +} + +ThreadIdNameManager::~ThreadIdNameManager() = default; + +ThreadIdNameManager* ThreadIdNameManager::GetInstance() { + return Singleton<ThreadIdNameManager, + LeakySingletonTraits<ThreadIdNameManager> >::get(); +} + +const char* ThreadIdNameManager::GetDefaultInternedString() { + return g_default_name->c_str(); +} + +void ThreadIdNameManager::RegisterThread(PlatformThreadHandle::Handle handle, + PlatformThreadId id) { + AutoLock locked(lock_); + thread_id_to_handle_[id] = handle; + thread_handle_to_interned_name_[handle] = + name_to_interned_name_[kDefaultName]; +} + +void ThreadIdNameManager::AddObserver(Observer* obs) { + AutoLock locked(lock_); + DCHECK(!base::Contains(observers_, obs)); + observers_.push_back(obs); +} + +void ThreadIdNameManager::RemoveObserver(Observer* obs) { + AutoLock locked(lock_); + DCHECK(base::Contains(observers_, obs)); + base::Erase(observers_, obs); +} + +void ThreadIdNameManager::SetName(const std::string& name) { + PlatformThreadId id = PlatformThread::CurrentId(); + std::string* leaked_str = nullptr; + { + AutoLock locked(lock_); + auto iter = name_to_interned_name_.find(name); + if (iter != name_to_interned_name_.end()) { + leaked_str = iter->second; + } else { + leaked_str = new std::string(name); + name_to_interned_name_[name] = leaked_str; + } + + auto id_to_handle_iter = thread_id_to_handle_.find(id); + + GetThreadNameTLS().Set(const_cast<char*>(leaked_str->c_str())); + for (Observer* obs : observers_) + obs->OnThreadNameChanged(leaked_str->c_str()); + + // The main thread of a process will not be created as a Thread object which + // means there is no PlatformThreadHandler registered. + if (id_to_handle_iter == thread_id_to_handle_.end()) { + main_process_name_ = leaked_str; + main_process_id_ = id; + return; + } + thread_handle_to_interned_name_[id_to_handle_iter->second] = leaked_str; + } + + // Add the leaked thread name to heap profiler context tracker. The name added + // is valid for the lifetime of the process. AllocationContextTracker cannot + // call GetName(which holds a lock) during the first allocation because it can + // cause a deadlock when the first allocation happens in the + // ThreadIdNameManager itself when holding the lock. + trace_event::AllocationContextTracker::SetCurrentThreadName( + leaked_str->c_str()); +} + +const char* ThreadIdNameManager::GetName(PlatformThreadId id) { + AutoLock locked(lock_); + + if (id == main_process_id_) + return main_process_name_->c_str(); + + auto id_to_handle_iter = thread_id_to_handle_.find(id); + if (id_to_handle_iter == thread_id_to_handle_.end()) + return name_to_interned_name_[kDefaultName]->c_str(); + + auto handle_to_name_iter = + thread_handle_to_interned_name_.find(id_to_handle_iter->second); + return handle_to_name_iter->second->c_str(); +} + +const char* ThreadIdNameManager::GetNameForCurrentThread() { + const char* name = reinterpret_cast<const char*>(GetThreadNameTLS().Get()); + return name ? name : kDefaultName; +} + +void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle, + PlatformThreadId id) { + AutoLock locked(lock_); + auto handle_to_name_iter = thread_handle_to_interned_name_.find(handle); + + DCHECK(handle_to_name_iter != thread_handle_to_interned_name_.end()); + thread_handle_to_interned_name_.erase(handle_to_name_iter); + + auto id_to_handle_iter = thread_id_to_handle_.find(id); + DCHECK((id_to_handle_iter!= thread_id_to_handle_.end())); + // The given |id| may have been re-used by the system. Make sure the + // mapping points to the provided |handle| before removal. + if (id_to_handle_iter->second != handle) + return; + + thread_id_to_handle_.erase(id_to_handle_iter); +} + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/thread_id_name_manager.h b/security/sandbox/chromium/base/threading/thread_id_name_manager.h new file mode 100644 index 0000000000..e413da5d03 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_id_name_manager.h @@ -0,0 +1,94 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_THREAD_ID_NAME_MANAGER_H_ +#define BASE_THREADING_THREAD_ID_NAME_MANAGER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/base_export.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/synchronization/lock.h" +#include "base/threading/platform_thread.h" + +namespace base { + +template <typename T> +struct DefaultSingletonTraits; + +class BASE_EXPORT ThreadIdNameManager { + public: + static ThreadIdNameManager* GetInstance(); + + static const char* GetDefaultInternedString(); + + class BASE_EXPORT Observer { + public: + virtual ~Observer(); + + // Called on the thread whose name is changing, immediately after the name + // is set. |name| is a pointer to a C string that is guaranteed to remain + // valid for the duration of the process. + // + // NOTE: Will be called while ThreadIdNameManager's lock is held, so don't + // call back into it. + virtual void OnThreadNameChanged(const char* name) = 0; + }; + + // Register the mapping between a thread |id| and |handle|. + void RegisterThread(PlatformThreadHandle::Handle handle, PlatformThreadId id); + + void AddObserver(Observer*); + void RemoveObserver(Observer*); + + // Set the name for the current thread. + void SetName(const std::string& name); + + // Get the name for the given id. + const char* GetName(PlatformThreadId id); + + // Unlike |GetName|, this method using TLS and avoids touching |lock_|. + const char* GetNameForCurrentThread(); + + // Remove the name for the given id. + void RemoveName(PlatformThreadHandle::Handle handle, PlatformThreadId id); + + private: + friend struct DefaultSingletonTraits<ThreadIdNameManager>; + + typedef std::map<PlatformThreadId, PlatformThreadHandle::Handle> + ThreadIdToHandleMap; + typedef std::map<PlatformThreadHandle::Handle, std::string*> + ThreadHandleToInternedNameMap; + typedef std::map<std::string, std::string*> NameToInternedNameMap; + + ThreadIdNameManager(); + ~ThreadIdNameManager(); + + // lock_ protects the name_to_interned_name_, thread_id_to_handle_ and + // thread_handle_to_interned_name_ maps. + Lock lock_; + + NameToInternedNameMap name_to_interned_name_; + ThreadIdToHandleMap thread_id_to_handle_; + ThreadHandleToInternedNameMap thread_handle_to_interned_name_; + + // Treat the main process specially as there is no PlatformThreadHandle. + std::string* main_process_name_; + PlatformThreadId main_process_id_; + + // There's no point using a base::ObserverList behind a lock, so we just use + // an std::vector instead. + std::vector<Observer*> observers_; + + DISALLOW_COPY_AND_ASSIGN(ThreadIdNameManager); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_ID_NAME_MANAGER_H_ diff --git a/security/sandbox/chromium/base/threading/thread_local.h b/security/sandbox/chromium/base/threading/thread_local.h new file mode 100644 index 0000000000..f9762050b6 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_local.h @@ -0,0 +1,136 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: Thread local storage is a bit tricky to get right. Please make sure +// that this is really the proper solution for what you're trying to achieve. +// Don't prematurely optimize, most likely you can just use a Lock. +// +// These classes implement a wrapper around ThreadLocalStorage::Slot. On +// construction, they will allocate a TLS slot, and free the TLS slot on +// destruction. No memory management (creation or destruction) is handled. This +// means for uses of ThreadLocalPointer, you must correctly manage the memory +// yourself, these classes will not destroy the pointer for you. There are no +// at-thread-exit actions taken by these classes. +// +// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or +// destruction, so memory management must be handled elsewhere. The first call +// to Get() on a thread will return NULL. You can update the pointer with a call +// to Set(). +// +// ThreadLocalBoolean wraps a bool. It will default to false if it has never +// been set otherwise with Set(). +// +// Thread Safety: An instance of ThreadLocalStorage is completely thread safe +// once it has been created. If you want to dynamically create an instance, you +// must of course properly deal with safety and race conditions. +// +// In Android, the system TLS is limited. +// +// Example usage: +// // My class is logically attached to a single thread. We cache a pointer +// // on the thread it was created on, so we can implement current(). +// MyClass::MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this); +// } +// +// MyClass::~MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL); +// } +// +// // Return the current MyClass associated with the calling thread, can be +// // NULL if there isn't a MyClass associated. +// MyClass* MyClass::current() { +// return Singleton<ThreadLocalPointer<MyClass> >::get()->Get(); +// } + +#ifndef BASE_THREADING_THREAD_LOCAL_H_ +#define BASE_THREADING_THREAD_LOCAL_H_ + +#include <memory> + +#include "base/logging.h" +#include "base/macros.h" +#include "base/threading/thread_local_internal.h" +#include "base/threading/thread_local_storage.h" + +namespace base { + +template <typename T> +class ThreadLocalPointer { + public: + ThreadLocalPointer() = default; + ~ThreadLocalPointer() = default; + + T* Get() const { return static_cast<T*>(slot_.Get()); } + + void Set(T* ptr) { + slot_.Set(const_cast<void*>(static_cast<const void*>(ptr))); + } + + private: + ThreadLocalStorage::Slot slot_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<T>); +}; + +// A ThreadLocalOwnedPointer<T> is like a ThreadLocalPointer<T> except that +// pointers handed to it are owned and automatically deleted during their +// associated thread's exit phase (or when replaced if Set() is invoked multiple +// times on the same thread). +// The ThreadLocalOwnedPointer instance itself can only be destroyed when no +// threads, other than the one it is destroyed on, have remaining state set in +// it. Typically this means that ThreadLocalOwnedPointer instances are held in +// static storage or at the very least only recycled in the single-threaded +// phase between tests in the same process. +#if DCHECK_IS_ON() +template <typename T> +using ThreadLocalOwnedPointer = internal::CheckedThreadLocalOwnedPointer<T>; +#else // DCHECK_IS_ON() +template <typename T> +class ThreadLocalOwnedPointer { + public: + ThreadLocalOwnedPointer() = default; + + ~ThreadLocalOwnedPointer() { + // Assume that this thread is the only one with potential state left. This + // is verified in ~CheckedThreadLocalOwnedPointer(). + Set(nullptr); + } + + T* Get() const { return static_cast<T*>(slot_.Get()); } + + void Set(std::unique_ptr<T> ptr) { + delete Get(); + slot_.Set(const_cast<void*>(static_cast<const void*>(ptr.release()))); + } + + private: + static void DeleteTlsPtr(void* ptr) { delete static_cast<T*>(ptr); } + + ThreadLocalStorage::Slot slot_{&DeleteTlsPtr}; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalOwnedPointer<T>); +}; +#endif // DCHECK_IS_ON() + +class ThreadLocalBoolean { + public: + ThreadLocalBoolean() = default; + ~ThreadLocalBoolean() = default; + + bool Get() const { return tlp_.Get() != nullptr; } + + void Set(bool val) { tlp_.Set(val ? this : nullptr); } + + private: + ThreadLocalPointer<void> tlp_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_LOCAL_H_ diff --git a/security/sandbox/chromium/base/threading/thread_local_internal.h b/security/sandbox/chromium/base/threading/thread_local_internal.h new file mode 100644 index 0000000000..6f7fdc9768 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_local_internal.h @@ -0,0 +1,80 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_THREAD_LOCAL_INTERNAL_H_ +#define BASE_THREADING_THREAD_LOCAL_INTERNAL_H_ + +#if DCHECK_IS_ON() + +#include <atomic> +#include <memory> + +#include "base/macros.h" +#include "base/threading/thread_local_storage.h" + +namespace base { +namespace internal { + +// A version of ThreadLocalOwnedPointer which verifies that it's only destroyed +// when no threads, other than the one it is destroyed on, have remaining state +// set in it. A ThreadLocalOwnedPointer instance being destroyed too early would +// result in leaks per unregistering the TLS slot (and thus the DeleteTlsPtr +// hook). +template <typename T> +class CheckedThreadLocalOwnedPointer { + public: + CheckedThreadLocalOwnedPointer() = default; + + ~CheckedThreadLocalOwnedPointer() { + Set(nullptr); + + DCHECK_EQ(num_assigned_threads_.load(std::memory_order_relaxed), 0) + << "Memory leak: Must join all threads or release all associated " + "thread-local slots before ~ThreadLocalOwnedPointer"; + } + + T* Get() const { + PtrTracker* const ptr_tracker = static_cast<PtrTracker*>(slot_.Get()); + return ptr_tracker ? ptr_tracker->ptr_.get() : nullptr; + } + + void Set(std::unique_ptr<T> ptr) { + delete static_cast<PtrTracker*>(slot_.Get()); + if (ptr) + slot_.Set(new PtrTracker(this, std::move(ptr))); + else + slot_.Set(nullptr); + } + + private: + struct PtrTracker { + public: + PtrTracker(CheckedThreadLocalOwnedPointer<T>* outer, std::unique_ptr<T> ptr) + : outer_(outer), ptr_(std::move(ptr)) { + outer_->num_assigned_threads_.fetch_add(1, std::memory_order_relaxed); + } + + ~PtrTracker() { + outer_->num_assigned_threads_.fetch_sub(1, std::memory_order_relaxed); + } + + CheckedThreadLocalOwnedPointer<T>* const outer_; + const std::unique_ptr<T> ptr_; + }; + + static void DeleteTlsPtr(void* ptr) { delete static_cast<PtrTracker*>(ptr); } + + ThreadLocalStorage::Slot slot_{&DeleteTlsPtr}; + + std::atomic_int num_assigned_threads_{0}; + + DISALLOW_COPY_AND_ASSIGN(CheckedThreadLocalOwnedPointer<T>); +}; + +} // namespace internal +} // namespace base + +#endif // DCHECK_IS_ON() + +#endif // BASE_THREADING_THREAD_LOCAL_INTERNAL_H_ diff --git a/security/sandbox/chromium/base/threading/thread_local_storage.cc b/security/sandbox/chromium/base/threading/thread_local_storage.cc new file mode 100644 index 0000000000..204f34c272 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_local_storage.cc @@ -0,0 +1,461 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/thread_local_storage.h" + +#include "base/atomicops.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/synchronization/lock.h" +#include "build/build_config.h" + +using base::internal::PlatformThreadLocalStorage; + +// Chrome Thread Local Storage (TLS) +// +// This TLS system allows Chrome to use a single OS level TLS slot process-wide, +// and allows us to control the slot limits instead of being at the mercy of the +// platform. To do this, Chrome TLS replicates an array commonly found in the OS +// thread metadata. +// +// Overview: +// +// OS TLS Slots Per-Thread Per-Process Global +// ... +// [] Chrome TLS Array Chrome TLS Metadata +// [] ----------> [][][][][ ][][][][] [][][][][ ][][][][] +// [] | | +// ... V V +// Metadata Version Slot Information +// Your Data! +// +// Using a single OS TLS slot, Chrome TLS allocates an array on demand for the +// lifetime of each thread that requests Chrome TLS data. Each per-thread TLS +// array matches the length of the per-process global metadata array. +// +// A per-process global TLS metadata array tracks information about each item in +// the per-thread array: +// * Status: Tracks if the slot is allocated or free to assign. +// * Destructor: An optional destructor to call on thread destruction for that +// specific slot. +// * Version: Tracks the current version of the TLS slot. Each TLS slot +// allocation is associated with a unique version number. +// +// Most OS TLS APIs guarantee that a newly allocated TLS slot is +// initialized to 0 for all threads. The Chrome TLS system provides +// this guarantee by tracking the version for each TLS slot here +// on each per-thread Chrome TLS array entry. Threads that access +// a slot with a mismatched version will receive 0 as their value. +// The metadata version is incremented when the client frees a +// slot. The per-thread metadata version is updated when a client +// writes to the slot. This scheme allows for constant time +// invalidation and avoids the need to iterate through each Chrome +// TLS array to mark the slot as zero. +// +// Just like an OS TLS API, clients of the Chrome TLS are responsible for +// managing any necessary lifetime of the data in their slots. The only +// convenience provided is automatic destruction when a thread ends. If a client +// frees a slot, that client is responsible for destroying the data in the slot. + +namespace { +// In order to make TLS destructors work, we need to keep around a function +// pointer to the destructor for each slot. We keep this array of pointers in a +// global (static) array. +// We use the single OS-level TLS slot (giving us one pointer per thread) to +// hold a pointer to a per-thread array (table) of slots that we allocate to +// Chromium consumers. + +// g_native_tls_key is the one native TLS that we use. It stores our table. +base::subtle::Atomic32 g_native_tls_key = + PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES; + +// The OS TLS slot has the following states. The TLS slot's lower 2 bits contain +// the state, the upper bits the TlsVectorEntry*. +// * kUninitialized: Any call to Slot::Get()/Set() will create the base +// per-thread TLS state. kUninitialized must be null. +// * kInUse: value has been created and is in use. +// * kDestroying: Set when the thread is exiting prior to deleting any of the +// values stored in the TlsVectorEntry*. This state is necessary so that +// sequence/task checks won't be done while in the process of deleting the +// tls entries (see comments in SequenceCheckerImpl for more details). +// * kDestroyed: All of the values in the vector have been deallocated and +// the TlsVectorEntry has been deleted. +// +// Final States: +// * Windows: kDestroyed. Windows does not iterate through the OS TLS to clean +// up the values. +// * POSIX: kUninitialized. POSIX iterates through TLS until all slots contain +// nullptr. +// +// More details on this design: +// We need some type of thread-local state to indicate that the TLS system has +// been destroyed. To do so, we leverage the multi-pass nature of destruction +// of pthread_key. +// +// a) After destruction of TLS system, we set the pthread_key to a sentinel +// kDestroyed. +// b) All calls to Slot::Get() DCHECK that the state is not kDestroyed, and +// any system which might potentially invoke Slot::Get() after destruction +// of TLS must check ThreadLocalStorage::ThreadIsBeingDestroyed(). +// c) After a full pass of the pthread_keys, on the next invocation of +// ConstructTlsVector(), we'll then set the key to nullptr. +// d) At this stage, the TLS system is back in its uninitialized state. +// e) If in the second pass of destruction of pthread_keys something were to +// re-initialize TLS [this should never happen! Since the only code which +// uses Chrome TLS is Chrome controlled, we should really be striving for +// single-pass destruction], then TLS will be re-initialized and then go +// through the 2-pass destruction system again. Everything should just +// work (TM). + +// The state of the tls-entry. +enum class TlsVectorState { + kUninitialized = 0, + + // In the process of destroying the entries in the vector. + kDestroying, + + // All of the entries and the vector has been destroyed. + kDestroyed, + + // The vector has been initialized and is in use. + kInUse, + + kMaxValue = kInUse +}; + +// Bit-mask used to store TlsVectorState. +constexpr uintptr_t kVectorStateBitMask = 3; +static_assert(static_cast<int>(TlsVectorState::kMaxValue) <= + kVectorStateBitMask, + "number of states must fit in header"); +static_assert(static_cast<int>(TlsVectorState::kUninitialized) == 0, + "kUninitialized must be null"); + +// The maximum number of slots in our thread local storage stack. +constexpr int kThreadLocalStorageSize = 256; + +enum TlsStatus { + FREE, + IN_USE, +}; + +struct TlsMetadata { + TlsStatus status; + base::ThreadLocalStorage::TLSDestructorFunc destructor; + // Incremented every time a slot is reused. Used to detect reuse of slots. + uint32_t version; +}; + +struct TlsVectorEntry { + void* data; + uint32_t version; +}; + +// This lock isn't needed until after we've constructed the per-thread TLS +// vector, so it's safe to use. +base::Lock* GetTLSMetadataLock() { + static auto* lock = new base::Lock(); + return lock; +} +TlsMetadata g_tls_metadata[kThreadLocalStorageSize]; +size_t g_last_assigned_slot = 0; + +// The maximum number of times to try to clear slots by calling destructors. +// Use pthread naming convention for clarity. +constexpr int kMaxDestructorIterations = kThreadLocalStorageSize; + +// Sets the value and state of the vector. +void SetTlsVectorValue(PlatformThreadLocalStorage::TLSKey key, + TlsVectorEntry* tls_data, + TlsVectorState state) { + DCHECK(tls_data || (state == TlsVectorState::kUninitialized) || + (state == TlsVectorState::kDestroyed)); + PlatformThreadLocalStorage::SetTLSValue( + key, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(tls_data) | + static_cast<uintptr_t>(state))); +} + +// Returns the tls vector and current state from the raw tls value. +TlsVectorState GetTlsVectorStateAndValue(void* tls_value, + TlsVectorEntry** entry = nullptr) { + if (entry) { + *entry = reinterpret_cast<TlsVectorEntry*>( + reinterpret_cast<uintptr_t>(tls_value) & ~kVectorStateBitMask); + } + return static_cast<TlsVectorState>(reinterpret_cast<uintptr_t>(tls_value) & + kVectorStateBitMask); +} + +// Returns the tls vector and state using the tls key. +TlsVectorState GetTlsVectorStateAndValue(PlatformThreadLocalStorage::TLSKey key, + TlsVectorEntry** entry = nullptr) { + return GetTlsVectorStateAndValue(PlatformThreadLocalStorage::GetTLSValue(key), + entry); +} + +// This function is called to initialize our entire Chromium TLS system. +// It may be called very early, and we need to complete most all of the setup +// (initialization) before calling *any* memory allocator functions, which may +// recursively depend on this initialization. +// As a result, we use Atomics, and avoid anything (like a singleton) that might +// require memory allocations. +TlsVectorEntry* ConstructTlsVector() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) { + CHECK(PlatformThreadLocalStorage::AllocTLS(&key)); + + // The TLS_KEY_OUT_OF_INDEXES is used to find out whether the key is set or + // not in NoBarrier_CompareAndSwap, but Posix doesn't have invalid key, we + // define an almost impossible value be it. + // If we really get TLS_KEY_OUT_OF_INDEXES as value of key, just alloc + // another TLS slot. + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) { + PlatformThreadLocalStorage::TLSKey tmp = key; + CHECK(PlatformThreadLocalStorage::AllocTLS(&key) && + key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES); + PlatformThreadLocalStorage::FreeTLS(tmp); + } + // Atomically test-and-set the tls_key. If the key is + // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as + // another thread already did our dirty work. + if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES != + static_cast<PlatformThreadLocalStorage::TLSKey>( + base::subtle::NoBarrier_CompareAndSwap( + &g_native_tls_key, + PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key))) { + // We've been shortcut. Another thread replaced g_native_tls_key first so + // we need to destroy our index and use the one the other thread got + // first. + PlatformThreadLocalStorage::FreeTLS(key); + key = base::subtle::NoBarrier_Load(&g_native_tls_key); + } + } + CHECK_EQ(GetTlsVectorStateAndValue(key), TlsVectorState::kUninitialized); + + // Some allocators, such as TCMalloc, make use of thread local storage. As a + // result, any attempt to call new (or malloc) will lazily cause such a system + // to initialize, which will include registering for a TLS key. If we are not + // careful here, then that request to create a key will call new back, and + // we'll have an infinite loop. We avoid that as follows: Use a stack + // allocated vector, so that we don't have dependence on our allocator until + // our service is in place. (i.e., don't even call new until after we're + // setup) + TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize]; + memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data)); + // Ensure that any rentrant calls change the temp version. + SetTlsVectorValue(key, stack_allocated_tls_data, TlsVectorState::kInUse); + + // Allocate an array to store our data. + TlsVectorEntry* tls_data = new TlsVectorEntry[kThreadLocalStorageSize]; + memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data)); + SetTlsVectorValue(key, tls_data, TlsVectorState::kInUse); + return tls_data; +} + +void OnThreadExitInternal(TlsVectorEntry* tls_data) { + DCHECK(tls_data); + // Some allocators, such as TCMalloc, use TLS. As a result, when a thread + // terminates, one of the destructor calls we make may be to shut down an + // allocator. We have to be careful that after we've shutdown all of the known + // destructors (perchance including an allocator), that we don't call the + // allocator and cause it to resurrect itself (with no possibly destructor + // call to follow). We handle this problem as follows: Switch to using a stack + // allocated vector, so that we don't have dependence on our allocator after + // we have called all g_tls_metadata destructors. (i.e., don't even call + // delete[] after we're done with destructors.) + TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize]; + memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data)); + // Ensure that any re-entrant calls change the temp version. + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + SetTlsVectorValue(key, stack_allocated_tls_data, TlsVectorState::kDestroying); + delete[] tls_data; // Our last dependence on an allocator. + + // Snapshot the TLS Metadata so we don't have to lock on every access. + TlsMetadata tls_metadata[kThreadLocalStorageSize]; + { + base::AutoLock auto_lock(*GetTLSMetadataLock()); + memcpy(tls_metadata, g_tls_metadata, sizeof(g_tls_metadata)); + } + + int remaining_attempts = kMaxDestructorIterations; + bool need_to_scan_destructors = true; + while (need_to_scan_destructors) { + need_to_scan_destructors = false; + // Try to destroy the first-created-slot (which is slot 1) in our last + // destructor call. That user was able to function, and define a slot with + // no other services running, so perhaps it is a basic service (like an + // allocator) and should also be destroyed last. If we get the order wrong, + // then we'll iterate several more times, so it is really not that critical + // (but it might help). + for (int slot = 0; slot < kThreadLocalStorageSize; ++slot) { + void* tls_value = stack_allocated_tls_data[slot].data; + if (!tls_value || tls_metadata[slot].status == TlsStatus::FREE || + stack_allocated_tls_data[slot].version != tls_metadata[slot].version) + continue; + + base::ThreadLocalStorage::TLSDestructorFunc destructor = + tls_metadata[slot].destructor; + if (!destructor) + continue; + stack_allocated_tls_data[slot].data = nullptr; // pre-clear the slot. + destructor(tls_value); + // Any destructor might have called a different service, which then set a + // different slot to a non-null value. Hence we need to check the whole + // vector again. This is a pthread standard. + need_to_scan_destructors = true; + } + if (--remaining_attempts <= 0) { + NOTREACHED(); // Destructors might not have been called. + break; + } + } + + // Remove our stack allocated vector. + SetTlsVectorValue(key, nullptr, TlsVectorState::kDestroyed); +} + +} // namespace + +namespace base { + +namespace internal { + +#if defined(OS_WIN) +void PlatformThreadLocalStorage::OnThreadExit() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) + return; + TlsVectorEntry* tls_vector = nullptr; + const TlsVectorState state = GetTlsVectorStateAndValue(key, &tls_vector); + + // On Windows, thread destruction callbacks are only invoked once per module, + // so there should be no way that this could be invoked twice. + DCHECK_NE(state, TlsVectorState::kDestroyed); + + // Maybe we have never initialized TLS for this thread. + if (state == TlsVectorState::kUninitialized) + return; + OnThreadExitInternal(tls_vector); +} +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +void PlatformThreadLocalStorage::OnThreadExit(void* value) { + // On posix this function may be called twice. The first pass calls dtors and + // sets state to kDestroyed. The second pass sets kDestroyed to + // kUninitialized. + TlsVectorEntry* tls_vector = nullptr; + const TlsVectorState state = GetTlsVectorStateAndValue(value, &tls_vector); + if (state == TlsVectorState::kDestroyed) { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + SetTlsVectorValue(key, nullptr, TlsVectorState::kUninitialized); + return; + } + + OnThreadExitInternal(tls_vector); +} +#endif // defined(OS_WIN) + +} // namespace internal + +// static +bool ThreadLocalStorage::HasBeenDestroyed() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) + return false; + const TlsVectorState state = GetTlsVectorStateAndValue(key); + return state == TlsVectorState::kDestroying || + state == TlsVectorState::kDestroyed; +} + +void ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES || + GetTlsVectorStateAndValue(key) == TlsVectorState::kUninitialized) { + ConstructTlsVector(); + } + + // Grab a new slot. + { + base::AutoLock auto_lock(*GetTLSMetadataLock()); + for (int i = 0; i < kThreadLocalStorageSize; ++i) { + // Tracking the last assigned slot is an attempt to find the next + // available slot within one iteration. Under normal usage, slots remain + // in use for the lifetime of the process (otherwise before we reclaimed + // slots, we would have run out of slots). This makes it highly likely the + // next slot is going to be a free slot. + size_t slot_candidate = + (g_last_assigned_slot + 1 + i) % kThreadLocalStorageSize; + if (g_tls_metadata[slot_candidate].status == TlsStatus::FREE) { + g_tls_metadata[slot_candidate].status = TlsStatus::IN_USE; + g_tls_metadata[slot_candidate].destructor = destructor; + g_last_assigned_slot = slot_candidate; + DCHECK_EQ(kInvalidSlotValue, slot_); + slot_ = slot_candidate; + version_ = g_tls_metadata[slot_candidate].version; + break; + } + } + } + CHECK_NE(slot_, kInvalidSlotValue); + CHECK_LT(slot_, kThreadLocalStorageSize); +} + +void ThreadLocalStorage::Slot::Free() { + DCHECK_NE(slot_, kInvalidSlotValue); + DCHECK_LT(slot_, kThreadLocalStorageSize); + { + base::AutoLock auto_lock(*GetTLSMetadataLock()); + g_tls_metadata[slot_].status = TlsStatus::FREE; + g_tls_metadata[slot_].destructor = nullptr; + ++(g_tls_metadata[slot_].version); + } + slot_ = kInvalidSlotValue; +} + +void* ThreadLocalStorage::Slot::Get() const { + TlsVectorEntry* tls_data = nullptr; + const TlsVectorState state = GetTlsVectorStateAndValue( + base::subtle::NoBarrier_Load(&g_native_tls_key), &tls_data); + DCHECK_NE(state, TlsVectorState::kDestroyed); + if (!tls_data) + return nullptr; + DCHECK_NE(slot_, kInvalidSlotValue); + DCHECK_LT(slot_, kThreadLocalStorageSize); + // Version mismatches means this slot was previously freed. + if (tls_data[slot_].version != version_) + return nullptr; + return tls_data[slot_].data; +} + +void ThreadLocalStorage::Slot::Set(void* value) { + TlsVectorEntry* tls_data = nullptr; + const TlsVectorState state = GetTlsVectorStateAndValue( + base::subtle::NoBarrier_Load(&g_native_tls_key), &tls_data); + DCHECK_NE(state, TlsVectorState::kDestroyed); + if (!tls_data) { + if (!value) + return; + tls_data = ConstructTlsVector(); + } + DCHECK_NE(slot_, kInvalidSlotValue); + DCHECK_LT(slot_, kThreadLocalStorageSize); + tls_data[slot_].data = value; + tls_data[slot_].version = version_; +} + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { + Initialize(destructor); +} + +ThreadLocalStorage::Slot::~Slot() { + Free(); +} + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/thread_local_storage.h b/security/sandbox/chromium/base/threading/thread_local_storage.h new file mode 100644 index 0000000000..73b845ef56 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_local_storage.h @@ -0,0 +1,175 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_THREAD_LOCAL_STORAGE_H_ +#define BASE_THREADING_THREAD_LOCAL_STORAGE_H_ + +#include <stdint.h> + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/macros.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/win/windows_types.h" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#include <pthread.h> +#endif + +namespace ui { +class TLSDestructionCheckerForX11; +} + +namespace base { + +class SamplingHeapProfiler; + +namespace debug { +class GlobalActivityTracker; +} // namespace debug + +namespace trace_event { +class MallocDumpProvider; +} // namespace trace_event + +namespace internal { + +class ThreadLocalStorageTestInternal; + +// WARNING: You should *NOT* use this class directly. +// PlatformThreadLocalStorage is a low-level abstraction of the OS's TLS +// interface. Instead, you should use one of the following: +// * ThreadLocalBoolean (from thread_local.h) for booleans. +// * ThreadLocalPointer (from thread_local.h) for pointers. +// * ThreadLocalStorage::StaticSlot/Slot for more direct control of the slot. +class BASE_EXPORT PlatformThreadLocalStorage { + public: + +#if defined(OS_WIN) + typedef unsigned long TLSKey; + enum : unsigned { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES }; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + typedef pthread_key_t TLSKey; + // The following is a "reserved key" which is used in our generic Chromium + // ThreadLocalStorage implementation. We expect that an OS will not return + // such a key, but if it is returned (i.e., the OS tries to allocate it) we + // will just request another key. + enum { TLS_KEY_OUT_OF_INDEXES = 0x7FFFFFFF }; +#endif + + // The following methods need to be supported on each OS platform, so that + // the Chromium ThreadLocalStore functionality can be constructed. + // Chromium will use these methods to acquire a single OS slot, and then use + // that to support a much larger number of Chromium slots (independent of the + // OS restrictions). + // The following returns true if it successfully is able to return an OS + // key in |key|. + static bool AllocTLS(TLSKey* key); + // Note: FreeTLS() doesn't have to be called, it is fine with this leak, OS + // might not reuse released slot, you might just reset the TLS value with + // SetTLSValue(). + static void FreeTLS(TLSKey key); + static void SetTLSValue(TLSKey key, void* value); + static void* GetTLSValue(TLSKey key) { +#if defined(OS_WIN) + return TlsGetValue(key); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return pthread_getspecific(key); +#endif + } + + // Each platform (OS implementation) is required to call this method on each + // terminating thread when the thread is about to terminate. This method + // will then call all registered destructors for slots in Chromium + // ThreadLocalStorage, until there are no slot values remaining as having + // been set on this thread. + // Destructors may end up being called multiple times on a terminating + // thread, as other destructors may re-set slots that were previously + // destroyed. +#if defined(OS_WIN) + // Since Windows which doesn't support TLS destructor, the implementation + // should use GetTLSValue() to retrieve the value of TLS slot. + static void OnThreadExit(); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // |Value| is the data stored in TLS slot, The implementation can't use + // GetTLSValue() to retrieve the value of slot as it has already been reset + // in Posix. + static void OnThreadExit(void* value); +#endif +}; + +} // namespace internal + +// Wrapper for thread local storage. This class doesn't do much except provide +// an API for portability. +class BASE_EXPORT ThreadLocalStorage { + public: + // Prototype for the TLS destructor function, which can be optionally used to + // cleanup thread local storage on thread exit. 'value' is the data that is + // stored in thread local storage. + typedef void (*TLSDestructorFunc)(void* value); + + // A key representing one value stored in TLS. Use as a class member or a + // local variable. If you need a static storage duration variable, use the + // following pattern with a NoDestructor<Slot>: + // void MyDestructorFunc(void* value); + // ThreadLocalStorage::Slot& ImportantContentTLS() { + // static NoDestructor<ThreadLocalStorage::Slot> important_content_tls( + // &MyDestructorFunc); + // return *important_content_tls; + // } + class BASE_EXPORT Slot final { + public: + // |destructor| is a pointer to a function to perform per-thread cleanup of + // this object. If set to nullptr, no cleanup is done for this TLS slot. + explicit Slot(TLSDestructorFunc destructor = nullptr); + // If a destructor was set for this slot, removes the destructor so that + // remaining threads exiting will not free data. + ~Slot(); + + // Get the thread-local value stored in slot 'slot'. + // Values are guaranteed to initially be zero. + void* Get() const; + + // Set the thread-local value stored in slot 'slot' to + // value 'value'. + void Set(void* value); + + private: + void Initialize(TLSDestructorFunc destructor); + void Free(); + + static constexpr int kInvalidSlotValue = -1; + int slot_ = kInvalidSlotValue; + uint32_t version_ = 0; + + DISALLOW_COPY_AND_ASSIGN(Slot); + }; + + private: + // In most cases, most callers should not need access to HasBeenDestroyed(). + // If you are working in code that runs during thread destruction, contact the + // base OWNERs for advice and then make a friend request. + // + // Returns |true| if Chrome's implementation of TLS is being or has been + // destroyed during thread destruction. Attempting to call Slot::Get() during + // destruction is disallowed and will hit a DCHECK. Any code that relies on + // TLS during thread destruction must first check this method before calling + // Slot::Get(). + friend class SequenceCheckerImpl; + friend class SamplingHeapProfiler; + friend class ThreadCheckerImpl; + friend class internal::ThreadLocalStorageTestInternal; + friend class trace_event::MallocDumpProvider; + friend class debug::GlobalActivityTracker; + friend class ui::TLSDestructionCheckerForX11; + static bool HasBeenDestroyed(); + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_LOCAL_STORAGE_H_ diff --git a/security/sandbox/chromium/base/threading/thread_local_storage_posix.cc b/security/sandbox/chromium/base/threading/thread_local_storage_posix.cc new file mode 100644 index 0000000000..89edeee1d2 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_local_storage_posix.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/thread_local_storage.h" + +#include "base/logging.h" + +namespace base { + +namespace internal { + +bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { + return !pthread_key_create(key, + base::internal::PlatformThreadLocalStorage::OnThreadExit); +} + +void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { + int ret = pthread_key_delete(key); + DCHECK_EQ(ret, 0); +} + +void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { + int ret = pthread_setspecific(key, value); + DCHECK_EQ(ret, 0); +} + +} // namespace internal + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/thread_local_storage_win.cc b/security/sandbox/chromium/base/threading/thread_local_storage_win.cc new file mode 100644 index 0000000000..a9aec31da5 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_local_storage_win.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/thread_local_storage.h" + +#include <windows.h> + +#include "base/logging.h" + +namespace base { + +namespace internal { + +bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { + TLSKey value = TlsAlloc(); + if (value != TLS_OUT_OF_INDEXES) { + *key = value; + return true; + } + return false; +} + +void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { + BOOL ret = TlsFree(key); + DCHECK(ret); +} + +void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { + BOOL ret = TlsSetValue(key, value); + DCHECK(ret); +} + +} // namespace internal + +} // namespace base + +// Thread Termination Callbacks. +// Windows doesn't support a per-thread destructor with its +// TLS primitives. So, we build it manually by inserting a +// function to be called on each thread's exit. +// This magic is from http://www.codeproject.com/threads/tls.asp +// and it works for VC++ 7.0 and later. + +// Force a reference to _tls_used to make the linker create the TLS directory +// if it's not already there. (e.g. if __declspec(thread) is not used). +// Force a reference to p_thread_callback_base to prevent whole program +// optimization from discarding the variable. +#ifdef _WIN64 + +#pragma comment(linker, "/INCLUDE:_tls_used") +#pragma comment(linker, "/INCLUDE:p_thread_callback_base") + +#else // _WIN64 + +#pragma comment(linker, "/INCLUDE:__tls_used") +#pragma comment(linker, "/INCLUDE:_p_thread_callback_base") + +#endif // _WIN64 + +// Static callback function to call with each thread termination. +void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) { + // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+ + // and on W2K and W2K3. So don't assume it is sent. + if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) + base::internal::PlatformThreadLocalStorage::OnThreadExit(); +} + +// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are +// called automatically by the OS loader code (not the CRT) when the module is +// loaded and on thread creation. They are NOT called if the module has been +// loaded by a LoadLibrary() call. It must have implicitly been loaded at +// process startup. +// By implicitly loaded, I mean that it is directly referenced by the main EXE +// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being +// implicitly loaded. +// +// See VC\crt\src\tlssup.c for reference. + +// extern "C" suppresses C++ name mangling so we know the symbol name for the +// linker /INCLUDE:symbol pragma above. +extern "C" { +// The linker must not discard p_thread_callback_base. (We force a reference +// to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If +// this variable is discarded, the OnThreadExit function will never be called. +#ifdef _WIN64 + +// .CRT section is merged with .rdata on x64 so it must be constant data. +#pragma const_seg(".CRT$XLB") +// When defining a const variable, it must have external linkage to be sure the +// linker doesn't discard it. +extern const PIMAGE_TLS_CALLBACK p_thread_callback_base; +const PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit; + +// Reset the default section. +#pragma const_seg() + +#else // _WIN64 + +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit; + +// Reset the default section. +#pragma data_seg() + +#endif // _WIN64 +} // extern "C" diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.cc b/security/sandbox/chromium/base/threading/thread_restrictions.cc new file mode 100644 index 0000000000..75c37eab4f --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_restrictions.cc @@ -0,0 +1,258 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/threading/thread_restrictions.h" + +#if DCHECK_IS_ON() + +#include "base/debug/stack_trace.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/threading/thread_local.h" +#include "build/build_config.h" + +namespace base { + +std::ostream& operator<<(std::ostream&out, const ThreadLocalBoolean& tl) { + out << "currently set to " << (tl.Get() ? "true" : "false"); + return out; +} + +namespace { + +#if defined(OS_NACL) || defined(OS_ANDROID) +// NaCL doesn't support stack sampling and Android is slow at stack +// sampling and this causes timeouts (crbug.com/959139). +using ThreadLocalBooleanWithStacks = ThreadLocalBoolean; +#else +class ThreadLocalBooleanWithStacks { + public: + ThreadLocalBooleanWithStacks() = default; + + bool Get() const { return bool_.Get(); } + + void Set(bool val) { + stack_.Set(std::make_unique<debug::StackTrace>()); + bool_.Set(val); + } + + friend std::ostream& operator<<(std::ostream& out, + const ThreadLocalBooleanWithStacks& tl) { + out << tl.bool_ << " by "; + + if (!tl.stack_.Get()) + return out << "default value\n"; + out << "\n"; + tl.stack_.Get()->OutputToStream(&out); + return out; + } + + private: + ThreadLocalBoolean bool_; + ThreadLocalOwnedPointer<debug::StackTrace> stack_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanWithStacks); +}; +#endif // defined(OS_NACL) + +LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_blocking_disallowed = + LAZY_INSTANCE_INITIALIZER; + +LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_singleton_disallowed = + LAZY_INSTANCE_INITIALIZER; + +LazyInstance<ThreadLocalBooleanWithStacks>::Leaky + g_base_sync_primitives_disallowed = LAZY_INSTANCE_INITIALIZER; + +LazyInstance<ThreadLocalBooleanWithStacks>::Leaky + g_cpu_intensive_work_disallowed = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace internal { + +void AssertBlockingAllowed() { + DCHECK(!g_blocking_disallowed.Get().Get()) + << "Function marked as blocking was called from a scope that disallows " + "blocking! If this task is running inside the ThreadPool, it needs " + "to have MayBlock() in its TaskTraits. Otherwise, consider making " + "this blocking work asynchronous or, as a last resort, you may use " + "ScopedAllowBlocking (see its documentation for best practices).\n" + << "g_blocking_disallowed " << g_blocking_disallowed.Get(); +} + +} // namespace internal + +void DisallowBlocking() { + g_blocking_disallowed.Get().Set(true); +} + +ScopedDisallowBlocking::ScopedDisallowBlocking() + : was_disallowed_(g_blocking_disallowed.Get().Get()) { + g_blocking_disallowed.Get().Set(true); +} + +ScopedDisallowBlocking::~ScopedDisallowBlocking() { + DCHECK(g_blocking_disallowed.Get().Get()); + g_blocking_disallowed.Get().Set(was_disallowed_); +} + +ScopedAllowBlocking::ScopedAllowBlocking() + : was_disallowed_(g_blocking_disallowed.Get().Get()) { + g_blocking_disallowed.Get().Set(false); +} + +ScopedAllowBlocking::~ScopedAllowBlocking() { + DCHECK(!g_blocking_disallowed.Get().Get()); + g_blocking_disallowed.Get().Set(was_disallowed_); +} + +void DisallowBaseSyncPrimitives() { + g_base_sync_primitives_disallowed.Get().Set(true); +} + +ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives() + : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) { + DCHECK(!g_blocking_disallowed.Get().Get()) + << "To allow //base sync primitives in a scope where blocking is " + "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n" + << "g_blocking_disallowed " << g_blocking_disallowed.Get(); + g_base_sync_primitives_disallowed.Get().Set(false); +} + +ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() { + DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); + g_base_sync_primitives_disallowed.Get().Set(was_disallowed_); +} + +ScopedAllowBaseSyncPrimitivesOutsideBlockingScope:: + ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() + : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) { + g_base_sync_primitives_disallowed.Get().Set(false); +} + +ScopedAllowBaseSyncPrimitivesOutsideBlockingScope:: + ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() { + DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); + g_base_sync_primitives_disallowed.Get().Set(was_disallowed_); +} + +ScopedAllowBaseSyncPrimitivesForTesting:: + ScopedAllowBaseSyncPrimitivesForTesting() + : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) { + g_base_sync_primitives_disallowed.Get().Set(false); +} + +ScopedAllowBaseSyncPrimitivesForTesting:: + ~ScopedAllowBaseSyncPrimitivesForTesting() { + DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); + g_base_sync_primitives_disallowed.Get().Set(was_disallowed_); +} + +ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting() + : was_disallowed_base_sync_(g_base_sync_primitives_disallowed.Get().Get()), + was_disallowed_blocking_(g_blocking_disallowed.Get().Get()), + was_disallowed_cpu_(g_cpu_intensive_work_disallowed.Get().Get()) { + g_base_sync_primitives_disallowed.Get().Set(false); + g_blocking_disallowed.Get().Set(false); + g_cpu_intensive_work_disallowed.Get().Set(false); +} + +ScopedAllowUnresponsiveTasksForTesting:: + ~ScopedAllowUnresponsiveTasksForTesting() { + DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); + DCHECK(!g_blocking_disallowed.Get().Get()); + DCHECK(!g_cpu_intensive_work_disallowed.Get().Get()); + g_base_sync_primitives_disallowed.Get().Set(was_disallowed_base_sync_); + g_blocking_disallowed.Get().Set(was_disallowed_blocking_); + g_cpu_intensive_work_disallowed.Get().Set(was_disallowed_cpu_); +} + +namespace internal { + +void AssertBaseSyncPrimitivesAllowed() { + DCHECK(!g_base_sync_primitives_disallowed.Get().Get()) + << "Waiting on a //base sync primitive is not allowed on this thread to " + "prevent jank and deadlock. If waiting on a //base sync primitive is " + "unavoidable, do it within the scope of a " + "ScopedAllowBaseSyncPrimitives. If in a test, " + "use ScopedAllowBaseSyncPrimitivesForTesting.\n" + << "g_base_sync_primitives_disallowed " + << g_base_sync_primitives_disallowed.Get() + << "It can be useful to know that g_blocking_disallowed is " + << g_blocking_disallowed.Get(); +} + +void ResetThreadRestrictionsForTesting() { + g_blocking_disallowed.Get().Set(false); + g_singleton_disallowed.Get().Set(false); + g_base_sync_primitives_disallowed.Get().Set(false); + g_cpu_intensive_work_disallowed.Get().Set(false); +} + +} // namespace internal + +void AssertLongCPUWorkAllowed() { + DCHECK(!g_cpu_intensive_work_disallowed.Get().Get()) + << "Function marked as CPU intensive was called from a scope that " + "disallows this kind of work! Consider making this work " + "asynchronous.\n" + << "g_cpu_intensive_work_disallowed " + << g_cpu_intensive_work_disallowed.Get(); +} + +void DisallowUnresponsiveTasks() { + DisallowBlocking(); + DisallowBaseSyncPrimitives(); + g_cpu_intensive_work_disallowed.Get().Set(true); +} + +ThreadRestrictions::ScopedAllowIO::ScopedAllowIO() + : was_allowed_(SetIOAllowed(true)) {} + +ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() { + SetIOAllowed(was_allowed_); +} + +// static +bool ThreadRestrictions::SetIOAllowed(bool allowed) { + bool previous_disallowed = g_blocking_disallowed.Get().Get(); + g_blocking_disallowed.Get().Set(!allowed); + return !previous_disallowed; +} + +// static +bool ThreadRestrictions::SetSingletonAllowed(bool allowed) { + bool previous_disallowed = g_singleton_disallowed.Get().Get(); + g_singleton_disallowed.Get().Set(!allowed); + return !previous_disallowed; +} + +// static +void ThreadRestrictions::AssertSingletonAllowed() { + DCHECK(!g_singleton_disallowed.Get().Get()) + << "LazyInstance/Singleton is not allowed to be used on this thread. " + "Most likely it's because this thread is not joinable (or the current " + "task is running with TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN " + "semantics), so AtExitManager may have deleted the object on " + "shutdown, leading to a potential shutdown crash. If you need to use " + "the object from this context, it'll have to be updated to use Leaky " + "traits.\n" + << "g_singleton_disallowed " << g_singleton_disallowed.Get(); +} + +// static +void ThreadRestrictions::DisallowWaiting() { + DisallowBaseSyncPrimitives(); +} + +bool ThreadRestrictions::SetWaitAllowed(bool allowed) { + bool previous_disallowed = g_base_sync_primitives_disallowed.Get().Get(); + g_base_sync_primitives_disallowed.Get().Set(!allowed); + return !previous_disallowed; +} + +} // namespace base + +#endif // DCHECK_IS_ON() diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.h b/security/sandbox/chromium/base/threading/thread_restrictions.h new file mode 100644 index 0000000000..55047c5b40 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_restrictions.h @@ -0,0 +1,680 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_THREAD_RESTRICTIONS_H_ +#define BASE_THREADING_THREAD_RESTRICTIONS_H_ + +#include "base/base_export.h" +#include "base/gtest_prod_util.h" +#include "base/logging.h" +#include "base/macros.h" + +// ----------------------------------------------------------------------------- +// Usage documentation +// ----------------------------------------------------------------------------- +// +// Overview: +// This file exposes functions to ban and allow certain slow operations +// on a per-thread basis. To annotate *usage* of such slow operations, refer to +// scoped_blocking_call.h instead. +// +// Specific allowances that can be controlled in this file are: +// - Blocking call: Refers to any call that causes the calling thread to wait +// off-CPU. It includes but is not limited to calls that wait on synchronous +// file I/O operations: read or write a file from disk, interact with a pipe +// or a socket, rename or delete a file, enumerate files in a directory, etc. +// Acquiring a low contention lock is not considered a blocking call. +// +// - Waiting on a //base sync primitive: Refers to calling one of these methods: +// - base::WaitableEvent::*Wait* +// - base::ConditionVariable::*Wait* +// - base::Process::WaitForExit* +// +// - Long CPU work: Refers to any code that takes more than 100 ms to +// run when there is no CPU contention and no hard page faults and therefore, +// is not suitable to run on a thread required to keep the browser responsive +// (where jank could be visible to the user). +// +// The following disallowance functions are offered: +// - DisallowBlocking(): Disallows blocking calls on the current thread. +// - DisallowBaseSyncPrimitives(): Disallows waiting on a //base sync primitive +// on the current thread. +// - DisallowUnresponsiveTasks() Disallows blocking calls, waiting on a //base +// sync primitive, and long cpu work on the current thread. +// +// In addition, scoped-allowance mechanisms are offered to make an exception +// within a scope for a behavior that is normally disallowed. +// - ScopedAllowBlocking(ForTesting): Allows blocking calls. +// - ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope): Allow +// waiting on a //base sync primitive. The OutsideBlockingScope suffix allows +// uses in a scope where blocking is also disallowed. +// +// Avoid using allowances outside of unit tests. In unit tests, use allowances +// with the suffix "ForTesting". +// +// Prefer making blocking calls from tasks posted to base::ThreadPoolInstance +// with base::MayBlock(). +// +// Instead of waiting on a WaitableEvent or a ConditionVariable, prefer putting +// the work that should happen after the wait in a continuation callback and +// post it from where the WaitableEvent or ConditionVariable would have been +// signaled. If something needs to be scheduled after many tasks have executed, +// use base::BarrierClosure. +// +// On Windows, join processes asynchronously using base::win::ObjectWatcher. +// +// Where unavoidable, put ScopedAllow* instances in the narrowest scope possible +// in the caller making the blocking call but no further down. For example: if a +// Cleanup() method needs to do a blocking call, document Cleanup() as blocking +// and add a ScopedAllowBlocking instance in callers that can't avoid making +// this call from a context where blocking is banned, as such: +// +// void Client::MyMethod() { +// (...) +// { +// // Blocking is okay here because XYZ. +// ScopedAllowBlocking allow_blocking; +// my_foo_->Cleanup(); +// } +// (...) +// } +// +// // This method can block. +// void Foo::Cleanup() { +// // Do NOT add the ScopedAllowBlocking in Cleanup() directly as that hides +// // its blocking nature from unknowing callers and defeats the purpose of +// // these checks. +// FlushStateToDisk(); +// } +// +// Note: In rare situations where the blocking call is an implementation detail +// (i.e. the impl makes a call that invokes AssertBlockingAllowed() but it +// somehow knows that in practice this will not block), it might be okay to hide +// the ScopedAllowBlocking instance in the impl with a comment explaining why +// that's okay. + +class BrowserProcessImpl; +class HistogramSynchronizer; +class KeyStorageLinux; +class NativeBackendKWallet; +class NativeDesktopMediaList; +class StartupTimeBomb; + +namespace android_webview { +class AwFormDatabaseService; +class CookieManager; +class ScopedAllowInitGLBindings; +class VizCompositorThreadRunnerWebView; +} +namespace audio { +class OutputDevice; +} +namespace blink { +class RTCVideoDecoderAdapter; +class RTCVideoEncoder; +class SourceStream; +class VideoFrameResourceProvider; +class WorkerThread; +namespace scheduler { +class WorkerThread; +} +} +namespace cc { +class CompletionEvent; +class TileTaskManagerImpl; +} +namespace chromeos { +class BlockingMethodCaller; +namespace system { +class StatisticsProviderImpl; +} +} +namespace chrome_browser_net { +class Predictor; +} +namespace chrome_cleaner { +class SystemReportComponent; +} +namespace content { +class BrowserGpuChannelHostFactory; +class BrowserMainLoop; +class BrowserProcessSubThread; +class BrowserShutdownProfileDumper; +class BrowserTestBase; +class CategorizedWorkerPool; +class DesktopCaptureDevice; +class InProcessUtilityThread; +class NestedMessagePumpAndroid; +class RenderProcessHostImpl; +class RenderWidgetHostViewMac; +class RTCVideoDecoder; +class SandboxHostLinux; +class ScopedAllowWaitForDebugURL; +class ServiceWorkerContextClient; +class SoftwareOutputDeviceMus; +class SynchronousCompositor; +class SynchronousCompositorHost; +class SynchronousCompositorSyncCallBridge; +class TextInputClientMac; +class WebContentsViewMac; +} // namespace content +namespace cronet { +class CronetPrefsManager; +class CronetURLRequestContext; +} // namespace cronet +namespace dbus { +class Bus; +} +namespace disk_cache { +class BackendImpl; +class InFlightIO; +} +namespace functions { +class ExecScriptScopedAllowBaseSyncPrimitives; +} +namespace history_report { +class HistoryReportJniBridge; +} +namespace gpu { +class GpuChannelHost; +} +namespace leveldb_env { +class DBTracker; +} +namespace media { +class AudioInputDevice; +class AudioOutputDevice; +class BlockingUrlProtocol; +class PaintCanvasVideoRenderer; +} +namespace memory_instrumentation { +class OSMetrics; +} +namespace midi { +class TaskService; // https://crbug.com/796830 +} +namespace module_installer { +class ScopedAllowModulePakLoad; +} +namespace mojo { +class CoreLibraryInitializer; +class SyncCallRestrictions; +namespace core { +class ScopedIPCSupport; +} +} +namespace printing { +class PrintJobWorker; +class PrinterQuery; +} +namespace rlz_lib { +class FinancialPing; +} +namespace syncer { +class GetLocalChangesRequest; +class HttpBridge; +class ModelSafeWorker; +} +namespace ui { +class CommandBufferClientImpl; +class CommandBufferLocal; +class GpuState; +class MaterialDesignController; +} +namespace weblayer { +class WebLayerPathProvider; +} +namespace net { +class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives; +class MultiThreadedProxyResolverScopedAllowJoinOnIO; +class NetworkChangeNotifierMac; +class NetworkConfigWatcherMacThread; +namespace internal { +class AddressTrackerLinux; +} +} + +namespace proxy_resolver { +class ScopedAllowThreadJoinForProxyResolverV8Tracing; +} + +namespace remoting { +class AutoThread; +namespace protocol { +class ScopedAllowThreadJoinForWebRtcTransport; +} +} + +namespace resource_coordinator { +class TabManagerDelegate; +} + +namespace service_manager { +class ServiceProcessLauncher; +} + +namespace shell_integration_linux { +class LaunchXdgUtilityScopedAllowBaseSyncPrimitives; +} + +namespace ui { +class WindowResizeHelperMac; +} + +namespace viz { +class HostGpuMemoryBufferManager; +} + +namespace vr { +class VrShell; +} + +namespace web { +class WebMainLoop; +class WebSubThread; +} + +namespace weblayer { +class ProfileImpl; +} + +namespace webrtc { +class DesktopConfigurationMonitor; +} + +namespace base { + +namespace sequence_manager { +namespace internal { +class TaskQueueImpl; +} +} // namespace sequence_manager + +namespace android { +class JavaHandlerThread; +} + +namespace internal { +class JobTaskSource; +class TaskTracker; +} + +class AdjustOOMScoreHelper; +class FileDescriptorWatcher; +class GetAppOutputScopedAllowBaseSyncPrimitives; +class ScopedAllowThreadRecallForStackSamplingProfiler; +class SimpleThread; +class StackSamplingProfiler; +class Thread; + +#if DCHECK_IS_ON() +#define INLINE_IF_DCHECK_IS_OFF BASE_EXPORT +#define EMPTY_BODY_IF_DCHECK_IS_OFF +#else +#define INLINE_IF_DCHECK_IS_OFF inline + +// The static_assert() eats follow-on semicolons. `= default` would work +// too, but it makes clang realize that all the Scoped classes are no-ops in +// non-dcheck builds and it starts emitting many -Wunused-variable warnings. +#define EMPTY_BODY_IF_DCHECK_IS_OFF \ + {} \ + static_assert(true, "") +#endif + +namespace internal { + +// Asserts that blocking calls are allowed in the current scope. This is an +// internal call, external code should use ScopedBlockingCall instead, which +// serves as a precise annotation of the scope that may/will block. +INLINE_IF_DCHECK_IS_OFF void AssertBlockingAllowed() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +} // namespace internal + +// Disallows blocking on the current thread. +INLINE_IF_DCHECK_IS_OFF void DisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; + +// Disallows blocking calls within its scope. +class BASE_EXPORT ScopedDisallowBlocking { + public: + ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; + + private: +#if DCHECK_IS_ON() + const bool was_disallowed_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedDisallowBlocking); +}; + +class BASE_EXPORT ScopedAllowBlocking { + private: + FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBlocking); + friend class ScopedAllowBlockingForTesting; + + // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting + // in unit tests to avoid the friend requirement. + friend class AdjustOOMScoreHelper; + friend class android_webview::ScopedAllowInitGLBindings; + friend class content::BrowserProcessSubThread; + friend class content::RenderWidgetHostViewMac; // http://crbug.com/121917 + friend class content::WebContentsViewMac; + friend class cronet::CronetPrefsManager; + friend class cronet::CronetURLRequestContext; + friend class memory_instrumentation::OSMetrics; + friend class module_installer::ScopedAllowModulePakLoad; + friend class mojo::CoreLibraryInitializer; + friend class printing::PrintJobWorker; + friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703 + friend class ui::MaterialDesignController; + friend class web::WebSubThread; + friend class StackSamplingProfiler; + friend class weblayer::ProfileImpl; + friend class content::RenderProcessHostImpl; + friend class weblayer::WebLayerPathProvider; + + ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; + +#if DCHECK_IS_ON() + const bool was_disallowed_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlocking); +}; + +class ScopedAllowBlockingForTesting { + public: + ScopedAllowBlockingForTesting() {} + ~ScopedAllowBlockingForTesting() {} + + private: +#if DCHECK_IS_ON() + ScopedAllowBlocking scoped_allow_blocking_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlockingForTesting); +}; + +INLINE_IF_DCHECK_IS_OFF void DisallowBaseSyncPrimitives() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +class BASE_EXPORT ScopedAllowBaseSyncPrimitives { + private: + // This can only be instantiated by friends. Use + // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend + // requirement. + FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, + ScopedAllowBaseSyncPrimitives); + FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, + ScopedAllowBaseSyncPrimitivesResetsState); + FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, + ScopedAllowBaseSyncPrimitivesWithBlockingDisallowed); + + // Allowed usage: + friend class SimpleThread; + friend class base::GetAppOutputScopedAllowBaseSyncPrimitives; + friend class blink::SourceStream; + friend class blink::WorkerThread; + friend class blink::scheduler::WorkerThread; + friend class chrome_cleaner::SystemReportComponent; + friend class content::BrowserMainLoop; + friend class content::BrowserProcessSubThread; + friend class content::ServiceWorkerContextClient; + friend class functions::ExecScriptScopedAllowBaseSyncPrimitives; + friend class history_report::HistoryReportJniBridge; + friend class internal::TaskTracker; + friend class leveldb_env::DBTracker; + friend class media::BlockingUrlProtocol; + friend class mojo::core::ScopedIPCSupport; + friend class net::MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives; + friend class rlz_lib::FinancialPing; + friend class shell_integration_linux:: + LaunchXdgUtilityScopedAllowBaseSyncPrimitives; + friend class syncer::HttpBridge; + friend class syncer::GetLocalChangesRequest; + friend class syncer::ModelSafeWorker; + friend class webrtc::DesktopConfigurationMonitor; + + // Usage that should be fixed: + friend class ::NativeBackendKWallet; // http://crbug.com/125331 + friend class ::chromeos::system:: + StatisticsProviderImpl; // http://crbug.com/125385 + friend class content::TextInputClientMac; // http://crbug.com/121917 + friend class blink::VideoFrameResourceProvider; // http://crbug.com/878070 + + ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF; + +#if DCHECK_IS_ON() + const bool was_disallowed_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitives); +}; + +class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope { + private: + // This can only be instantiated by friends. Use + // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend + // requirement. + FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, + ScopedAllowBaseSyncPrimitivesOutsideBlockingScope); + FRIEND_TEST_ALL_PREFIXES( + ThreadRestrictionsTest, + ScopedAllowBaseSyncPrimitivesOutsideBlockingScopeResetsState); + + // Allowed usage: + friend class ::BrowserProcessImpl; // http://crbug.com/125207 + friend class ::KeyStorageLinux; + friend class ::NativeDesktopMediaList; + friend class ::StartupTimeBomb; + friend class android::JavaHandlerThread; + friend class android_webview:: + AwFormDatabaseService; // http://crbug.com/904431 + friend class android_webview::CookieManager; + friend class android_webview::VizCompositorThreadRunnerWebView; + friend class audio::OutputDevice; + friend class base::sequence_manager::internal::TaskQueueImpl; + friend class base::FileDescriptorWatcher; + friend class base::internal::JobTaskSource; + friend class base::ScopedAllowThreadRecallForStackSamplingProfiler; + friend class base::StackSamplingProfiler; + friend class blink::RTCVideoDecoderAdapter; + friend class blink::RTCVideoEncoder; + friend class cc::TileTaskManagerImpl; + friend class content::CategorizedWorkerPool; + friend class content::DesktopCaptureDevice; + friend class content::InProcessUtilityThread; + friend class content::RTCVideoDecoder; + friend class content::SandboxHostLinux; + friend class content::ScopedAllowWaitForDebugURL; + friend class content::SynchronousCompositor; + friend class content::SynchronousCompositorHost; + friend class content::SynchronousCompositorSyncCallBridge; + friend class media::AudioInputDevice; + friend class media::AudioOutputDevice; + friend class media::PaintCanvasVideoRenderer; + friend class mojo::SyncCallRestrictions; + friend class net::NetworkConfigWatcherMacThread; + friend class viz::HostGpuMemoryBufferManager; + friend class vr::VrShell; + + // Usage that should be fixed: + friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360 + friend class base::Thread; // http://crbug.com/918039 + friend class cc::CompletionEvent; // http://crbug.com/902653 + friend class content:: + BrowserGpuChannelHostFactory; // http://crbug.com/125248 + friend class dbus::Bus; // http://crbug.com/125222 + friend class disk_cache::BackendImpl; // http://crbug.com/74623 + friend class disk_cache::InFlightIO; // http://crbug.com/74623 + friend class gpu::GpuChannelHost; // http://crbug.com/125264 + friend class remoting::protocol:: + ScopedAllowThreadJoinForWebRtcTransport; // http://crbug.com/660081 + friend class midi::TaskService; // https://crbug.com/796830 + friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097 + friend class net:: + MultiThreadedProxyResolverScopedAllowJoinOnIO; // http://crbug.com/69710 + friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097 + friend class printing::PrinterQuery; // http://crbug.com/66082 + friend class proxy_resolver:: + ScopedAllowThreadJoinForProxyResolverV8Tracing; // http://crbug.com/69710 + friend class remoting::AutoThread; // https://crbug.com/944316 + // Not used in production yet, https://crbug.com/844078. + friend class service_manager::ServiceProcessLauncher; + friend class ui::WindowResizeHelperMac; // http://crbug.com/902829 + + ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() + EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +#if DCHECK_IS_ON() + const bool was_disallowed_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope); +}; + +class BASE_EXPORT ScopedAllowBaseSyncPrimitivesForTesting { + public: + ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; + + private: +#if DCHECK_IS_ON() + const bool was_disallowed_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesForTesting); +}; + +// Counterpart to base::DisallowUnresponsiveTasks() for tests to allow them to +// block their thread after it was banned. +class BASE_EXPORT ScopedAllowUnresponsiveTasksForTesting { + public: + ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; + + private: +#if DCHECK_IS_ON() + const bool was_disallowed_base_sync_; + const bool was_disallowed_blocking_; + const bool was_disallowed_cpu_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowUnresponsiveTasksForTesting); +}; + +namespace internal { + +// Asserts that waiting on a //base sync primitive is allowed in the current +// scope. +INLINE_IF_DCHECK_IS_OFF void AssertBaseSyncPrimitivesAllowed() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +// Resets all thread restrictions on the current thread. +INLINE_IF_DCHECK_IS_OFF void ResetThreadRestrictionsForTesting() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +} // namespace internal + +// Asserts that running long CPU work is allowed in the current scope. +INLINE_IF_DCHECK_IS_OFF void AssertLongCPUWorkAllowed() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +INLINE_IF_DCHECK_IS_OFF void DisallowUnresponsiveTasks() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +class BASE_EXPORT ThreadRestrictions { + public: + // Constructing a ScopedAllowIO temporarily allows IO for the current + // thread. Doing this is almost certainly always incorrect. + // + // DEPRECATED. Use ScopedAllowBlocking(ForTesting). + class BASE_EXPORT ScopedAllowIO { + public: + ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF; + + private: +#if DCHECK_IS_ON() + const bool was_allowed_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO); + }; + +#if DCHECK_IS_ON() + // Set whether the current thread to make IO calls. + // Threads start out in the *allowed* state. + // Returns the previous value. + // + // DEPRECATED. Use ScopedAllowBlocking(ForTesting) or ScopedDisallowBlocking. + static bool SetIOAllowed(bool allowed); + + // Set whether the current thread can use singletons. Returns the previous + // value. + static bool SetSingletonAllowed(bool allowed); + + // Check whether the current thread is allowed to use singletons (Singleton / + // LazyInstance). DCHECKs if not. + static void AssertSingletonAllowed(); + + // Disable waiting on the current thread. Threads start out in the *allowed* + // state. Returns the previous value. + // + // DEPRECATED. Use DisallowBaseSyncPrimitives. + static void DisallowWaiting(); +#else + // Inline the empty definitions of these functions so that they can be + // compiled out. + static bool SetIOAllowed(bool allowed) { return true; } + static bool SetSingletonAllowed(bool allowed) { return true; } + static void AssertSingletonAllowed() {} + static void DisallowWaiting() {} +#endif + + private: + // DO NOT ADD ANY OTHER FRIEND STATEMENTS. + // BEGIN ALLOWED USAGE. + friend class content::BrowserMainLoop; + friend class content::BrowserShutdownProfileDumper; + friend class content::BrowserTestBase; + friend class content::ScopedAllowWaitForDebugURL; + friend class ::HistogramSynchronizer; + friend class internal::TaskTracker; + friend class web::WebMainLoop; + friend class MessagePumpDefault; + friend class PlatformThread; + friend class ui::CommandBufferClientImpl; + friend class ui::CommandBufferLocal; + friend class ui::GpuState; + + // END ALLOWED USAGE. + // BEGIN USAGE THAT NEEDS TO BE FIXED. + friend class chrome_browser_net::Predictor; // http://crbug.com/78451 +#if !defined(OFFICIAL_BUILD) + friend class content::SoftwareOutputDeviceMus; // Interim non-production code +#endif +// END USAGE THAT NEEDS TO BE FIXED. + +#if DCHECK_IS_ON() + // DEPRECATED. Use ScopedAllowBaseSyncPrimitives. + static bool SetWaitAllowed(bool allowed); +#else + static bool SetWaitAllowed(bool allowed) { return true; } +#endif + + DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions); +}; + +#undef INLINE_IF_DCHECK_IS_OFF +#undef EMPTY_BODY_IF_DCHECK_IS_OFF + +} // namespace base + +#endif // BASE_THREADING_THREAD_RESTRICTIONS_H_ |