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