• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/allocator/dispatcher/tls.h"
6 
7 #if USE_LOCAL_TLS_EMULATION()
8 
9 #include "base/check.h"
10 #include "base/dcheck_is_on.h"
11 #include "base/debug/crash_logging.h"
12 #include "base/immediate_crash.h"
13 #include "build/build_config.h"
14 
15 #include <sys/mman.h>
16 
17 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
18 #include <sys/prctl.h>
19 #endif
20 
21 namespace base::allocator::dispatcher::internal {
22 namespace {
GetCrashKeySize(const std::string & crash_key_name)23 base::debug::CrashKeySize GetCrashKeySize(const std::string& crash_key_name) {
24   if (std::size(crash_key_name) <= 32ul) {
25     return base::debug::CrashKeySize::Size32;
26   }
27   if (std::size(crash_key_name) <= 64ul) {
28     return base::debug::CrashKeySize::Size64;
29   }
30   if (std::size(crash_key_name) <= 256ul) {
31     return base::debug::CrashKeySize::Size256;
32   }
33   CHECK(std::size(crash_key_name) <= 1024ul);
34 
35   return base::debug::CrashKeySize::Size1024;
36 }
37 
38 #if DCHECK_IS_ON()
Swap(std::atomic_bool & lh_op,std::atomic_bool & rh_op)39 void Swap(std::atomic_bool& lh_op, std::atomic_bool& rh_op) {
40   auto lh_op_value = lh_op.load(std::memory_order_relaxed);
41   auto rh_op_value = rh_op.load(std::memory_order_relaxed);
42 
43   CHECK(lh_op.compare_exchange_strong(lh_op_value, rh_op_value));
44   CHECK(rh_op.compare_exchange_strong(rh_op_value, lh_op_value));
45 }
46 #endif
47 }  // namespace
48 
AllocateMemory(size_t size_in_bytes)49 void* MMapAllocator::AllocateMemory(size_t size_in_bytes) {
50   void* const mmap_res = mmap(nullptr, size_in_bytes, PROT_READ | PROT_WRITE,
51                               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
52 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
53 #if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
54   if (mmap_res != MAP_FAILED) {
55     // Allow the anonymous memory region allocated by mmap(MAP_ANONYMOUS) to
56     // be identified in /proc/$PID/smaps.  This helps improve visibility into
57     // Chromium's memory usage on Android.
58     prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, mmap_res, size_in_bytes,
59           "tls-mmap-allocator");
60   }
61 #endif
62 #endif
63 
64   return (mmap_res != MAP_FAILED) ? mmap_res : nullptr;
65 }
66 
FreeMemoryForTesting(void * pointer_to_allocated,size_t size_in_bytes)67 bool MMapAllocator::FreeMemoryForTesting(void* pointer_to_allocated,
68                                          size_t size_in_bytes) {
69   auto const munmap_res = munmap(pointer_to_allocated, size_in_bytes);
70   return (munmap_res == 0);
71 }
72 
73 PThreadTLSSystem::PThreadTLSSystem() = default;
74 
PThreadTLSSystem(PThreadTLSSystem && other)75 PThreadTLSSystem::PThreadTLSSystem(PThreadTLSSystem&& other) {
76   std::swap(crash_key_, other.crash_key_);
77   std::swap(data_access_key_, other.data_access_key_);
78 
79 #if DCHECK_IS_ON()
80   Swap(initialized_, other.initialized_);
81 #endif
82 }
83 
operator =(PThreadTLSSystem && other)84 PThreadTLSSystem& PThreadTLSSystem::operator=(PThreadTLSSystem&& other) {
85   std::swap(crash_key_, other.crash_key_);
86   std::swap(data_access_key_, other.data_access_key_);
87 
88 #if DCHECK_IS_ON()
89   Swap(initialized_, other.initialized_);
90 #endif
91 
92   return *this;
93 }
94 
Setup(OnThreadTerminationFunction thread_termination_function,const base::StringPiece instance_id)95 bool PThreadTLSSystem::Setup(
96     OnThreadTerminationFunction thread_termination_function,
97     const base::StringPiece instance_id) {
98 #if DCHECK_IS_ON()
99   // Initialize must happen outside of the allocation path. Therefore, it is
100   // secure to verify with DCHECK.
101   DCHECK(!initialized_.exchange(true, std::memory_order_acq_rel));
102 #endif
103 
104   auto const key_create_res =
105       pthread_key_create(&data_access_key_, thread_termination_function);
106 
107   // On some platforms creating a new pthread-key requires an allocation when a
108   // given number of keys has been created. I.e. in glibc this limit is denoted
109   // by PTHREAD_KEY_2NDLEVEL_SIZE. However, this value is neither present on all
110   // systems nor accessible from here. Hence, we do not do any checks here.
111   // However, we strongly recommend to setup the TLS system as early as possible
112   // to avoid exceeding this limit.
113 
114   // Some crashes might be caused by the initialization being performed too late
115   // and running into the problems mentioned above. Since there's no way to
116   // handle this issue programmatically, we include the key into the crashpad
117   // report to allow for later inspection.
118   std::string crash_key_name = "tls_system-";
119   crash_key_name += instance_id;
120 
121   crash_key_ = base::debug::AllocateCrashKeyString(
122       crash_key_name.c_str(), GetCrashKeySize(crash_key_name));
123   base::debug::SetCrashKeyString(crash_key_,
124                                  base::NumberToString(data_access_key_));
125 
126   return (0 == key_create_res);
127 }
128 
TearDownForTesting()129 bool PThreadTLSSystem::TearDownForTesting() {
130 #if DCHECK_IS_ON()
131   // TearDownForTesting must happen outside of the allocation path. Therefore,
132   // it is secure to verify with DCHECK.
133   DCHECK(initialized_.exchange(false, std::memory_order_acq_rel));
134 #endif
135 
136   base::debug::ClearCrashKeyString(crash_key_);
137   crash_key_ = nullptr;
138 
139   auto const key_delete_res = pthread_key_delete(data_access_key_);
140   return (0 == key_delete_res);
141 }
142 
GetThreadSpecificData()143 void* PThreadTLSSystem::GetThreadSpecificData() {
144 #if DCHECK_IS_ON()
145   if (!initialized_.load(std::memory_order_acquire)) {
146     return nullptr;
147   }
148 #endif
149 
150   return pthread_getspecific(data_access_key_);
151 }
152 
SetThreadSpecificData(void * data)153 bool PThreadTLSSystem::SetThreadSpecificData(void* data) {
154 #if DCHECK_IS_ON()
155   if (!initialized_.load(std::memory_order_acquire)) {
156     return false;
157   }
158 #endif
159 
160   return (0 == pthread_setspecific(data_access_key_, data));
161 }
162 
163 }  // namespace base::allocator::dispatcher::internal
164 
165 #endif  // USE_LOCAL_TLS_EMULATION()
166