]>
Commit | Line | Data |
---|---|---|
ef1b3fda KS |
1 | //=-- lsan_common_linux.cc ------------------------------------------------===// |
2 | // | |
3 | // This file is distributed under the University of Illinois Open Source | |
4 | // License. See LICENSE.TXT for details. | |
5 | // | |
6 | //===----------------------------------------------------------------------===// | |
7 | // | |
8 | // This file is a part of LeakSanitizer. | |
9 | // Implementation of common leak checking functionality. Linux-specific code. | |
10 | // | |
11 | //===----------------------------------------------------------------------===// | |
12 | ||
13 | #include "sanitizer_common/sanitizer_platform.h" | |
14 | #include "lsan_common.h" | |
15 | ||
16 | #if CAN_SANITIZE_LEAKS && SANITIZER_LINUX | |
17 | #include <link.h> | |
18 | ||
19 | #include "sanitizer_common/sanitizer_common.h" | |
20 | #include "sanitizer_common/sanitizer_linux.h" | |
21 | #include "sanitizer_common/sanitizer_stackdepot.h" | |
22 | ||
23 | namespace __lsan { | |
24 | ||
25 | static const char kLinkerName[] = "ld"; | |
26 | // We request 2 modules matching "ld", so we can print a warning if there's more | |
27 | // than one match. But only the first one is actually used. | |
28 | static char linker_placeholder[2 * sizeof(LoadedModule)] ALIGNED(64); | |
29 | static LoadedModule *linker = 0; | |
30 | ||
31 | static bool IsLinker(const char* full_name) { | |
32 | return LibraryNameIs(full_name, kLinkerName); | |
33 | } | |
34 | ||
35 | void InitializePlatformSpecificModules() { | |
36 | internal_memset(linker_placeholder, 0, sizeof(linker_placeholder)); | |
37 | uptr num_matches = GetListOfModules( | |
38 | reinterpret_cast<LoadedModule *>(linker_placeholder), 2, IsLinker); | |
39 | if (num_matches == 1) { | |
40 | linker = reinterpret_cast<LoadedModule *>(linker_placeholder); | |
41 | return; | |
42 | } | |
43 | if (num_matches == 0) | |
44 | Report("LeakSanitizer: Dynamic linker not found. " | |
45 | "TLS will not be handled correctly.\n"); | |
46 | else if (num_matches > 1) | |
47 | Report("LeakSanitizer: Multiple modules match \"%s\". " | |
48 | "TLS will not be handled correctly.\n", kLinkerName); | |
49 | linker = 0; | |
50 | } | |
51 | ||
52 | static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, | |
53 | void *data) { | |
54 | Frontier *frontier = reinterpret_cast<Frontier *>(data); | |
55 | for (uptr j = 0; j < info->dlpi_phnum; j++) { | |
56 | const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]); | |
57 | // We're looking for .data and .bss sections, which reside in writeable, | |
58 | // loadable segments. | |
59 | if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) || | |
60 | (phdr->p_memsz == 0)) | |
61 | continue; | |
62 | uptr begin = info->dlpi_addr + phdr->p_vaddr; | |
63 | uptr end = begin + phdr->p_memsz; | |
64 | uptr allocator_begin = 0, allocator_end = 0; | |
65 | GetAllocatorGlobalRange(&allocator_begin, &allocator_end); | |
66 | if (begin <= allocator_begin && allocator_begin < end) { | |
67 | CHECK_LE(allocator_begin, allocator_end); | |
68 | CHECK_LT(allocator_end, end); | |
69 | if (begin < allocator_begin) | |
70 | ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL", | |
71 | kReachable); | |
72 | if (allocator_end < end) | |
73 | ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL", | |
74 | kReachable); | |
75 | } else { | |
76 | ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable); | |
77 | } | |
78 | } | |
79 | return 0; | |
80 | } | |
81 | ||
82 | // Scans global variables for heap pointers. | |
83 | void ProcessGlobalRegions(Frontier *frontier) { | |
84 | // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of | |
85 | // deadlocking by running this under StopTheWorld. However, the lock is | |
86 | // reentrant, so we should be able to fix this by acquiring the lock before | |
87 | // suspending threads. | |
88 | dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); | |
89 | } | |
90 | ||
91 | static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) { | |
92 | CHECK(stack_id); | |
93 | uptr size = 0; | |
94 | const uptr *trace = map->Get(stack_id, &size); | |
95 | // The top frame is our malloc/calloc/etc. The next frame is the caller. | |
96 | if (size >= 2) | |
97 | return trace[1]; | |
98 | return 0; | |
99 | } | |
100 | ||
101 | struct ProcessPlatformAllocParam { | |
102 | Frontier *frontier; | |
103 | StackDepotReverseMap *stack_depot_reverse_map; | |
104 | }; | |
105 | ||
106 | // ForEachChunk callback. Identifies unreachable chunks which must be treated as | |
107 | // reachable. Marks them as reachable and adds them to the frontier. | |
108 | static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { | |
109 | CHECK(arg); | |
110 | ProcessPlatformAllocParam *param = | |
111 | reinterpret_cast<ProcessPlatformAllocParam *>(arg); | |
112 | chunk = GetUserBegin(chunk); | |
113 | LsanMetadata m(chunk); | |
114 | if (m.allocated() && m.tag() != kReachable) { | |
115 | u32 stack_id = m.stack_trace_id(); | |
116 | uptr caller_pc = 0; | |
117 | if (stack_id > 0) | |
118 | caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map); | |
119 | // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark | |
120 | // it as reachable, as we can't properly report its allocation stack anyway. | |
121 | if (caller_pc == 0 || linker->containsAddress(caller_pc)) { | |
122 | m.set_tag(kReachable); | |
123 | param->frontier->push_back(chunk); | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | // Handles dynamically allocated TLS blocks by treating all chunks allocated | |
129 | // from ld-linux.so as reachable. | |
130 | void ProcessPlatformSpecificAllocations(Frontier *frontier) { | |
131 | if (!flags()->use_tls) return; | |
132 | if (!linker) return; | |
133 | StackDepotReverseMap stack_depot_reverse_map; | |
134 | ProcessPlatformAllocParam arg = {frontier, &stack_depot_reverse_map}; | |
135 | ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg); | |
136 | } | |
137 | ||
138 | } // namespace __lsan | |
139 | #endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX |