1 | //=-- lsan_common.h -------------------------------------------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of LeakSanitizer. |
10 | // Private LSan header. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LSAN_COMMON_H |
15 | #define LSAN_COMMON_H |
16 | |
17 | #include "sanitizer_common/sanitizer_allocator.h" |
18 | #include "sanitizer_common/sanitizer_common.h" |
19 | #include "sanitizer_common/sanitizer_internal_defs.h" |
20 | #include "sanitizer_common/sanitizer_platform.h" |
21 | #include "sanitizer_common/sanitizer_range.h" |
22 | #include "sanitizer_common/sanitizer_stackdepot.h" |
23 | #include "sanitizer_common/sanitizer_stoptheworld.h" |
24 | #include "sanitizer_common/sanitizer_symbolizer.h" |
25 | #include "sanitizer_common/sanitizer_thread_registry.h" |
26 | |
27 | // LeakSanitizer relies on some Glibc's internals (e.g. TLS machinery) on Linux. |
28 | // Also, LSan doesn't like 32 bit architectures |
29 | // because of "small" (4 bytes) pointer size that leads to high false negative |
30 | // ratio on large leaks. But we still want to have it for some 32 bit arches |
31 | // (e.g. x86), see https://github.com/google/sanitizers/issues/403. |
32 | // To enable LeakSanitizer on a new architecture, one needs to implement the |
33 | // internal_clone function as well as (probably) adjust the TLS machinery for |
34 | // the new architecture inside the sanitizer library. |
35 | // Exclude leak-detection on arm32 for Android because `__aeabi_read_tp` |
36 | // is missing. This caused a link error. |
37 | #if SANITIZER_ANDROID && (__ANDROID_API__ < 28 || defined(__arm__)) |
38 | # define CAN_SANITIZE_LEAKS 0 |
39 | #elif (SANITIZER_LINUX || SANITIZER_APPLE) && (SANITIZER_WORDSIZE == 64) && \ |
40 | (defined(__x86_64__) || defined(__mips64) || defined(__aarch64__) || \ |
41 | defined(__powerpc64__) || defined(__s390x__)) |
42 | # define CAN_SANITIZE_LEAKS 1 |
43 | #elif defined(__i386__) && (SANITIZER_LINUX || SANITIZER_APPLE) |
44 | # define CAN_SANITIZE_LEAKS 1 |
45 | #elif defined(__arm__) && SANITIZER_LINUX |
46 | # define CAN_SANITIZE_LEAKS 1 |
47 | #elif SANITIZER_LOONGARCH64 && SANITIZER_LINUX |
48 | # define CAN_SANITIZE_LEAKS 1 |
49 | #elif SANITIZER_RISCV64 && SANITIZER_LINUX |
50 | # define CAN_SANITIZE_LEAKS 1 |
51 | #elif SANITIZER_NETBSD || SANITIZER_FUCHSIA |
52 | # define CAN_SANITIZE_LEAKS 1 |
53 | #else |
54 | # define CAN_SANITIZE_LEAKS 0 |
55 | #endif |
56 | |
57 | namespace __sanitizer { |
58 | class FlagParser; |
59 | class ThreadRegistry; |
60 | class ThreadContextBase; |
61 | struct DTLS; |
62 | } |
63 | |
64 | // This section defines function and class prototypes which must be implemented |
65 | // by the parent tool linking in LSan. There are implementations provided by the |
66 | // LSan library which will be linked in when LSan is used as a standalone tool. |
67 | namespace __lsan { |
68 | |
69 | // Chunk tags. |
70 | enum ChunkTag { |
71 | kDirectlyLeaked = 0, // default |
72 | kIndirectlyLeaked = 1, |
73 | kReachable = 2, |
74 | kIgnored = 3 |
75 | }; |
76 | |
77 | enum IgnoreObjectResult { |
78 | kIgnoreObjectSuccess, |
79 | kIgnoreObjectAlreadyIgnored, |
80 | kIgnoreObjectInvalid |
81 | }; |
82 | |
83 | //// -------------------------------------------------------------------------- |
84 | //// Poisoning prototypes. |
85 | //// -------------------------------------------------------------------------- |
86 | |
87 | // Returns true if [addr, addr + sizeof(void *)) is poisoned. |
88 | bool WordIsPoisoned(uptr addr); |
89 | |
90 | //// -------------------------------------------------------------------------- |
91 | //// Thread prototypes. |
92 | //// -------------------------------------------------------------------------- |
93 | |
94 | // Wrappers for ThreadRegistry access. |
95 | void LockThreads() SANITIZER_NO_THREAD_SAFETY_ANALYSIS; |
96 | void UnlockThreads() SANITIZER_NO_THREAD_SAFETY_ANALYSIS; |
97 | // If called from the main thread, updates the main thread's TID in the thread |
98 | // registry. We need this to handle processes that fork() without a subsequent |
99 | // exec(), which invalidates the recorded TID. To update it, we must call |
100 | // gettid() from the main thread. Our solution is to call this function before |
101 | // leak checking and also before every call to pthread_create() (to handle cases |
102 | // where leak checking is initiated from a non-main thread). |
103 | void EnsureMainThreadIDIsCorrect(); |
104 | |
105 | bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, |
106 | uptr *tls_begin, uptr *tls_end, uptr *cache_begin, |
107 | uptr *cache_end, DTLS **dtls); |
108 | void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches); |
109 | void GetThreadExtraStackRangesLocked(InternalMmapVector<Range> *ranges); |
110 | void GetThreadExtraStackRangesLocked(tid_t os_id, |
111 | InternalMmapVector<Range> *ranges); |
112 | void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs); |
113 | void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads); |
114 | void PrintThreads(); |
115 | |
116 | //// -------------------------------------------------------------------------- |
117 | //// Allocator prototypes. |
118 | //// -------------------------------------------------------------------------- |
119 | |
120 | // Wrappers for allocator's ForceLock()/ForceUnlock(). |
121 | void LockAllocator(); |
122 | void UnlockAllocator(); |
123 | |
124 | // Lock/unlock global mutext. |
125 | void LockGlobal(); |
126 | void UnlockGlobal(); |
127 | |
128 | // Returns the address range occupied by the global allocator object. |
129 | void GetAllocatorGlobalRange(uptr *begin, uptr *end); |
130 | // If p points into a chunk that has been allocated to the user, returns its |
131 | // user-visible address. Otherwise, returns 0. |
132 | uptr PointsIntoChunk(void *p); |
133 | // Returns address of user-visible chunk contained in this allocator chunk. |
134 | uptr GetUserBegin(uptr chunk); |
135 | // Returns user-visible address for chunk. If memory tagging is used this |
136 | // function will return the tagged address. |
137 | uptr GetUserAddr(uptr chunk); |
138 | |
139 | // Wrapper for chunk metadata operations. |
140 | class LsanMetadata { |
141 | public: |
142 | // Constructor accepts address of user-visible chunk. |
143 | explicit LsanMetadata(uptr chunk); |
144 | bool allocated() const; |
145 | ChunkTag tag() const; |
146 | void set_tag(ChunkTag value); |
147 | uptr requested_size() const; |
148 | u32 stack_trace_id() const; |
149 | |
150 | private: |
151 | void *metadata_; |
152 | }; |
153 | |
154 | // Iterate over all existing chunks. Allocator must be locked. |
155 | void ForEachChunk(ForEachChunkCallback callback, void *arg); |
156 | |
157 | // Helper for __lsan_ignore_object(). |
158 | IgnoreObjectResult IgnoreObject(const void *p); |
159 | |
160 | // The rest of the LSan interface which is implemented by library. |
161 | |
162 | struct ScopedStopTheWorldLock { |
163 | ScopedStopTheWorldLock() { |
164 | LockThreads(); |
165 | LockAllocator(); |
166 | } |
167 | |
168 | ~ScopedStopTheWorldLock() { |
169 | UnlockAllocator(); |
170 | UnlockThreads(); |
171 | } |
172 | |
173 | ScopedStopTheWorldLock &operator=(const ScopedStopTheWorldLock &) = delete; |
174 | ScopedStopTheWorldLock(const ScopedStopTheWorldLock &) = delete; |
175 | }; |
176 | |
177 | struct Flags { |
178 | #define LSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; |
179 | #include "lsan_flags.inc" |
180 | #undef LSAN_FLAG |
181 | |
182 | void SetDefaults(); |
183 | uptr pointer_alignment() const { |
184 | return use_unaligned ? 1 : sizeof(uptr); |
185 | } |
186 | }; |
187 | |
188 | extern Flags lsan_flags; |
189 | inline Flags *flags() { return &lsan_flags; } |
190 | void RegisterLsanFlags(FlagParser *parser, Flags *f); |
191 | |
192 | struct LeakedChunk { |
193 | uptr chunk; |
194 | u32 stack_trace_id; |
195 | uptr leaked_size; |
196 | ChunkTag tag; |
197 | }; |
198 | |
199 | using LeakedChunks = InternalMmapVector<LeakedChunk>; |
200 | |
201 | struct Leak { |
202 | u32 id; |
203 | uptr hit_count; |
204 | uptr total_size; |
205 | u32 stack_trace_id; |
206 | bool is_directly_leaked; |
207 | bool is_suppressed; |
208 | }; |
209 | |
210 | struct LeakedObject { |
211 | u32 leak_id; |
212 | uptr addr; |
213 | uptr size; |
214 | }; |
215 | |
216 | // Aggregates leaks by stack trace prefix. |
217 | class LeakReport { |
218 | public: |
219 | LeakReport() {} |
220 | void AddLeakedChunks(const LeakedChunks &chunks); |
221 | void ReportTopLeaks(uptr max_leaks); |
222 | void PrintSummary(); |
223 | uptr ApplySuppressions(); |
224 | uptr UnsuppressedLeakCount(); |
225 | uptr IndirectUnsuppressedLeakCount(); |
226 | |
227 | private: |
228 | void PrintReportForLeak(uptr index); |
229 | void PrintLeakedObjectsForLeak(uptr index); |
230 | |
231 | u32 next_id_ = 0; |
232 | InternalMmapVector<Leak> leaks_; |
233 | InternalMmapVector<LeakedObject> leaked_objects_; |
234 | }; |
235 | |
236 | typedef InternalMmapVector<uptr> Frontier; |
237 | |
238 | // Platform-specific functions. |
239 | void InitializePlatformSpecificModules(); |
240 | void ProcessGlobalRegions(Frontier *frontier); |
241 | void ProcessPlatformSpecificAllocations(Frontier *frontier); |
242 | |
243 | // LockStuffAndStopTheWorld can start to use Scan* calls to collect into |
244 | // this Frontier vector before the StopTheWorldCallback actually runs. |
245 | // This is used when the OS has a unified callback API for suspending |
246 | // threads and enumerating roots. |
247 | struct CheckForLeaksParam { |
248 | Frontier frontier; |
249 | LeakedChunks leaks; |
250 | tid_t caller_tid; |
251 | uptr caller_sp; |
252 | bool success = false; |
253 | }; |
254 | |
255 | using Region = Range; |
256 | |
257 | bool HasRootRegions(); |
258 | void ScanRootRegions(Frontier *frontier, |
259 | const InternalMmapVectorNoCtor<Region> ®ion); |
260 | // Run stoptheworld while holding any platform-specific locks, as well as the |
261 | // allocator and thread registry locks. |
262 | void LockStuffAndStopTheWorld(StopTheWorldCallback callback, |
263 | CheckForLeaksParam* argument); |
264 | |
265 | void ScanRangeForPointers(uptr begin, uptr end, |
266 | Frontier *frontier, |
267 | const char *region_type, ChunkTag tag); |
268 | void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier); |
269 | void ScanExtraStackRanges(const InternalMmapVector<Range> &ranges, |
270 | Frontier *frontier); |
271 | |
272 | // Functions called from the parent tool. |
273 | const char *MaybeCallLsanDefaultOptions(); |
274 | void InitCommonLsan(); |
275 | void DoLeakCheck(); |
276 | void DoRecoverableLeakCheckVoid(); |
277 | void DisableCounterUnderflow(); |
278 | bool DisabledInThisThread(); |
279 | |
280 | // Used to implement __lsan::ScopedDisabler. |
281 | void DisableInThisThread(); |
282 | void EnableInThisThread(); |
283 | // Can be used to ignore memory allocated by an intercepted |
284 | // function. |
285 | struct ScopedInterceptorDisabler { |
286 | ScopedInterceptorDisabler() { DisableInThisThread(); } |
287 | ~ScopedInterceptorDisabler() { EnableInThisThread(); } |
288 | }; |
289 | |
290 | // According to Itanium C++ ABI array cookie is a one word containing |
291 | // size of allocated array. |
292 | static inline bool IsItaniumABIArrayCookie(uptr chunk_beg, uptr chunk_size, |
293 | uptr addr) { |
294 | return chunk_size == sizeof(uptr) && chunk_beg + chunk_size == addr && |
295 | *reinterpret_cast<uptr *>(chunk_beg) == 0; |
296 | } |
297 | |
298 | // According to ARM C++ ABI array cookie consists of two words: |
299 | // struct array_cookie { |
300 | // std::size_t element_size; // element_size != 0 |
301 | // std::size_t element_count; |
302 | // }; |
303 | static inline bool IsARMABIArrayCookie(uptr chunk_beg, uptr chunk_size, |
304 | uptr addr) { |
305 | return chunk_size == 2 * sizeof(uptr) && chunk_beg + chunk_size == addr && |
306 | *reinterpret_cast<uptr *>(chunk_beg + sizeof(uptr)) == 0; |
307 | } |
308 | |
309 | // Special case for "new T[0]" where T is a type with DTOR. |
310 | // new T[0] will allocate a cookie (one or two words) for the array size (0) |
311 | // and store a pointer to the end of allocated chunk. The actual cookie layout |
312 | // varies between platforms according to their C++ ABI implementation. |
313 | inline bool IsSpecialCaseOfOperatorNew0(uptr chunk_beg, uptr chunk_size, |
314 | uptr addr) { |
315 | #if defined(__arm__) |
316 | return IsARMABIArrayCookie(chunk_beg, chunk_size, addr); |
317 | #else |
318 | return IsItaniumABIArrayCookie(chunk_beg, chunk_size, addr); |
319 | #endif |
320 | } |
321 | |
322 | // Return the linker module, if valid for the platform. |
323 | LoadedModule *GetLinker(); |
324 | |
325 | // Return true if LSan has finished leak checking and reported leaks. |
326 | bool HasReportedLeaks(); |
327 | |
328 | // Run platform-specific leak handlers. |
329 | void HandleLeaks(); |
330 | |
331 | } // namespace __lsan |
332 | |
333 | extern "C"{ |
334 | SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE |
335 | const char *__lsan_default_options(); |
336 | |
337 | SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE |
338 | int __lsan_is_turned_off(); |
339 | |
340 | SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE |
341 | const char *__lsan_default_suppressions(); |
342 | |
343 | SANITIZER_INTERFACE_ATTRIBUTE |
344 | void __lsan_register_root_region(const void *p, __lsan::uptr size); |
345 | |
346 | SANITIZER_INTERFACE_ATTRIBUTE |
347 | void __lsan_unregister_root_region(const void *p, __lsan::uptr size); |
348 | |
349 | } // extern "C" |
350 | |
351 | #endif // LSAN_COMMON_H |
352 |
Definitions
- ChunkTag
- IgnoreObjectResult
- LsanMetadata
- ScopedStopTheWorldLock
- ScopedStopTheWorldLock
- ~ScopedStopTheWorldLock
- operator=
- ScopedStopTheWorldLock
- Flags
- pointer_alignment
- flags
- LeakedChunk
- Leak
- LeakedObject
- LeakReport
- LeakReport
- CheckForLeaksParam
- ScopedInterceptorDisabler
- ScopedInterceptorDisabler
- ~ScopedInterceptorDisabler
- IsItaniumABIArrayCookie
- IsARMABIArrayCookie
Learn to use CMake with our Intro Training
Find out more