]>
Commit | Line | Data |
---|---|---|
b667dd70 | 1 | //===-- sanitizer_common.cpp ----------------------------------------------===// |
f35db108 | 2 | // |
b667dd70 ML |
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 | |
f35db108 WM |
6 | // |
7 | //===----------------------------------------------------------------------===// | |
8 | // | |
9 | // This file is shared between AddressSanitizer and ThreadSanitizer | |
10 | // run-time libraries. | |
11 | //===----------------------------------------------------------------------===// | |
12 | ||
13 | #include "sanitizer_common.h" | |
10189819 | 14 | #include "sanitizer_allocator_interface.h" |
696d846a | 15 | #include "sanitizer_allocator_internal.h" |
eac97531 | 16 | #include "sanitizer_atomic.h" |
df77f0e4 | 17 | #include "sanitizer_flags.h" |
f35db108 | 18 | #include "sanitizer_libc.h" |
696d846a | 19 | #include "sanitizer_placement_new.h" |
f35db108 WM |
20 | |
21 | namespace __sanitizer { | |
22 | ||
b4ab7d34 KS |
23 | const char *SanitizerToolName = "SanitizerTool"; |
24 | ||
696d846a | 25 | atomic_uint32_t current_verbosity; |
10189819 | 26 | uptr PageSizeCached; |
eac97531 | 27 | u32 NumberOfCPUsCached; |
4ba5ca46 | 28 | |
c4c16f74 KS |
29 | // PID of the tracer task in StopTheWorld. It shares the address space with the |
30 | // main process, but has a different PID and thus requires special handling. | |
31 | uptr stoptheworld_tracer_pid = 0; | |
32 | // Cached pid of parent process - if the parent process dies, we want to keep | |
33 | // writing to the same log file. | |
34 | uptr stoptheworld_tracer_ppid = 0; | |
35 | ||
696d846a | 36 | void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, |
10189819 MO |
37 | const char *mmap_type, error_t err, |
38 | bool raw_report) { | |
696d846a | 39 | static int recursion_count; |
eac97531 ML |
40 | if (SANITIZER_RTEMS || raw_report || recursion_count) { |
41 | // If we are on RTEMS or raw report is requested or we went into recursion, | |
42 | // just die. The Report() and CHECK calls below may call mmap recursively | |
43 | // and fail. | |
696d846a MO |
44 | RawWrite("ERROR: Failed to mmap\n"); |
45 | Die(); | |
46 | } | |
47 | recursion_count++; | |
48 | Report("ERROR: %s failed to " | |
55aea9f5 MO |
49 | "%s 0x%zx (%zd) bytes of %s (error code: %d)\n", |
50 | SanitizerToolName, mmap_type, size, size, mem_type, err); | |
10189819 | 51 | #if !SANITIZER_GO |
696d846a | 52 | DumpProcessMap(); |
55aea9f5 | 53 | #endif |
696d846a MO |
54 | UNREACHABLE("unable to mmap"); |
55 | } | |
56 | ||
ef1b3fda | 57 | typedef bool UptrComparisonFunction(const uptr &a, const uptr &b); |
10189819 | 58 | typedef bool U32ComparisonFunction(const u32 &a, const u32 &b); |
ef1b3fda | 59 | |
df77f0e4 KS |
60 | const char *StripPathPrefix(const char *filepath, |
61 | const char *strip_path_prefix) { | |
696d846a MO |
62 | if (!filepath) return nullptr; |
63 | if (!strip_path_prefix) return filepath; | |
64 | const char *res = filepath; | |
65 | if (const char *pos = internal_strstr(filepath, strip_path_prefix)) | |
66 | res = pos + internal_strlen(strip_path_prefix); | |
67 | if (res[0] == '.' && res[1] == '/') | |
68 | res += 2; | |
69 | return res; | |
df77f0e4 KS |
70 | } |
71 | ||
c5be964a | 72 | const char *StripModuleName(const char *module) { |
696d846a MO |
73 | if (!module) |
74 | return nullptr; | |
75 | if (SANITIZER_WINDOWS) { | |
76 | // On Windows, both slash and backslash are possible. | |
77 | // Pick the one that goes last. | |
78 | if (const char *bslash_pos = internal_strrchr(module, '\\')) | |
79 | return StripModuleName(bslash_pos + 1); | |
80 | } | |
81 | if (const char *slash_pos = internal_strrchr(module, '/')) { | |
c5be964a | 82 | return slash_pos + 1; |
696d846a | 83 | } |
c5be964a | 84 | return module; |
df77f0e4 KS |
85 | } |
86 | ||
5d3805fc | 87 | void ReportErrorSummary(const char *error_message, const char *alt_tool_name) { |
df77f0e4 KS |
88 | if (!common_flags()->print_summary) |
89 | return; | |
696d846a | 90 | InternalScopedString buff(kMaxSummaryLength); |
5d3805fc JJ |
91 | buff.append("SUMMARY: %s: %s", |
92 | alt_tool_name ? alt_tool_name : SanitizerToolName, error_message); | |
df77f0e4 KS |
93 | __sanitizer_report_error_summary(buff.data()); |
94 | } | |
95 | ||
10189819 MO |
96 | // Removes the ANSI escape sequences from the input string (in-place). |
97 | void RemoveANSIEscapeSequencesFromString(char *str) { | |
98 | if (!str) | |
99 | return; | |
100 | ||
101 | // We are going to remove the escape sequences in place. | |
102 | char *s = str; | |
103 | char *z = str; | |
104 | while (*s != '\0') { | |
105 | CHECK_GE(s, z); | |
106 | // Skip over ANSI escape sequences with pointer 's'. | |
107 | if (*s == '\033' && *(s + 1) == '[') { | |
108 | s = internal_strchrnul(s, 'm'); | |
109 | if (*s == '\0') { | |
110 | break; | |
111 | } | |
112 | s++; | |
113 | continue; | |
114 | } | |
115 | // 's' now points at a character we want to keep. Copy over the buffer | |
116 | // content if the escape sequence has been perviously skipped andadvance | |
117 | // both pointers. | |
118 | if (s != z) | |
119 | *z = *s; | |
120 | ||
121 | // If we have not seen an escape sequence, just advance both pointers. | |
122 | z++; | |
123 | s++; | |
124 | } | |
125 | ||
126 | // Null terminate the string. | |
127 | *z = '\0'; | |
128 | } | |
129 | ||
696d846a MO |
130 | void LoadedModule::set(const char *module_name, uptr base_address) { |
131 | clear(); | |
ef1b3fda KS |
132 | full_name_ = internal_strdup(module_name); |
133 | base_address_ = base_address; | |
696d846a MO |
134 | } |
135 | ||
5d3805fc JJ |
136 | void LoadedModule::set(const char *module_name, uptr base_address, |
137 | ModuleArch arch, u8 uuid[kModuleUUIDSize], | |
138 | bool instrumented) { | |
139 | set(module_name, base_address); | |
140 | arch_ = arch; | |
141 | internal_memcpy(uuid_, uuid, sizeof(uuid_)); | |
142 | instrumented_ = instrumented; | |
143 | } | |
144 | ||
696d846a MO |
145 | void LoadedModule::clear() { |
146 | InternalFree(full_name_); | |
5d3805fc JJ |
147 | base_address_ = 0; |
148 | max_executable_address_ = 0; | |
696d846a | 149 | full_name_ = nullptr; |
5d3805fc JJ |
150 | arch_ = kModuleArchUnknown; |
151 | internal_memset(uuid_, 0, kModuleUUIDSize); | |
152 | instrumented_ = false; | |
696d846a MO |
153 | while (!ranges_.empty()) { |
154 | AddressRange *r = ranges_.front(); | |
155 | ranges_.pop_front(); | |
156 | InternalFree(r); | |
157 | } | |
ef1b3fda KS |
158 | } |
159 | ||
5d3805fc JJ |
160 | void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable, |
161 | bool writable, const char *name) { | |
696d846a | 162 | void *mem = InternalAlloc(sizeof(AddressRange)); |
5d3805fc JJ |
163 | AddressRange *r = |
164 | new(mem) AddressRange(beg, end, executable, writable, name); | |
696d846a | 165 | ranges_.push_back(r); |
5d3805fc JJ |
166 | if (executable && end > max_executable_address_) |
167 | max_executable_address_ = end; | |
ef1b3fda KS |
168 | } |
169 | ||
170 | bool LoadedModule::containsAddress(uptr address) const { | |
10189819 MO |
171 | for (const AddressRange &r : ranges()) { |
172 | if (r.beg <= address && address < r.end) | |
ef1b3fda KS |
173 | return true; |
174 | } | |
175 | return false; | |
176 | } | |
177 | ||
dee5ea7a KS |
178 | static atomic_uintptr_t g_total_mmaped; |
179 | ||
180 | void IncreaseTotalMmap(uptr size) { | |
181 | if (!common_flags()->mmap_limit_mb) return; | |
182 | uptr total_mmaped = | |
183 | atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size; | |
696d846a MO |
184 | // Since for now mmap_limit_mb is not a user-facing flag, just kill |
185 | // a program. Use RAW_CHECK to avoid extra mmaps in reporting. | |
186 | RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb); | |
dee5ea7a KS |
187 | } |
188 | ||
189 | void DecreaseTotalMmap(uptr size) { | |
190 | if (!common_flags()->mmap_limit_mb) return; | |
191 | atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed); | |
192 | } | |
193 | ||
696d846a MO |
194 | bool TemplateMatch(const char *templ, const char *str) { |
195 | if ((!str) || str[0] == 0) | |
196 | return false; | |
197 | bool start = false; | |
198 | if (templ && templ[0] == '^') { | |
199 | start = true; | |
200 | templ++; | |
201 | } | |
202 | bool asterisk = false; | |
203 | while (templ && templ[0]) { | |
204 | if (templ[0] == '*') { | |
205 | templ++; | |
206 | start = false; | |
207 | asterisk = true; | |
208 | continue; | |
209 | } | |
210 | if (templ[0] == '$') | |
211 | return str[0] == 0 || asterisk; | |
212 | if (str[0] == 0) | |
213 | return false; | |
214 | char *tpos = (char*)internal_strchr(templ, '*'); | |
215 | char *tpos1 = (char*)internal_strchr(templ, '$'); | |
216 | if ((!tpos) || (tpos1 && tpos1 < tpos)) | |
217 | tpos = tpos1; | |
218 | if (tpos) | |
219 | tpos[0] = 0; | |
220 | const char *str0 = str; | |
221 | const char *spos = internal_strstr(str, templ); | |
222 | str = spos + internal_strlen(templ); | |
223 | templ = tpos; | |
224 | if (tpos) | |
225 | tpos[0] = tpos == tpos1 ? '$' : '*'; | |
226 | if (!spos) | |
227 | return false; | |
228 | if (start && spos != str0) | |
229 | return false; | |
230 | start = false; | |
231 | asterisk = false; | |
232 | } | |
233 | return true; | |
234 | } | |
f35db108 | 235 | |
696d846a MO |
236 | static char binary_name_cache_str[kMaxPathLength]; |
237 | static char process_name_cache_str[kMaxPathLength]; | |
238 | ||
239 | const char *GetProcessName() { | |
240 | return process_name_cache_str; | |
241 | } | |
242 | ||
243 | static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) { | |
244 | ReadLongProcessName(buf, buf_len); | |
245 | char *s = const_cast<char *>(StripModuleName(buf)); | |
246 | uptr len = internal_strlen(s); | |
247 | if (s != buf) { | |
248 | internal_memmove(buf, s, len); | |
249 | buf[len] = '\0'; | |
df77f0e4 | 250 | } |
696d846a MO |
251 | return len; |
252 | } | |
253 | ||
254 | void UpdateProcessName() { | |
255 | ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str)); | |
256 | } | |
257 | ||
258 | // Call once to make sure that binary_name_cache_str is initialized | |
259 | void CacheBinaryName() { | |
260 | if (binary_name_cache_str[0] != '\0') | |
261 | return; | |
262 | ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str)); | |
263 | ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str)); | |
264 | } | |
265 | ||
266 | uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) { | |
267 | CacheBinaryName(); | |
268 | uptr name_len = internal_strlen(binary_name_cache_str); | |
269 | name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1; | |
270 | if (buf_len == 0) | |
271 | return 0; | |
272 | internal_memcpy(buf, binary_name_cache_str, name_len); | |
273 | buf[name_len] = '\0'; | |
274 | return name_len; | |
275 | } | |
276 | ||
3c6331c2 | 277 | #if !SANITIZER_GO |
10189819 MO |
278 | void PrintCmdline() { |
279 | char **argv = GetArgv(); | |
280 | if (!argv) return; | |
281 | Printf("\nCommand: "); | |
282 | for (uptr i = 0; argv[i]; ++i) | |
283 | Printf("%s ", argv[i]); | |
284 | Printf("\n\n"); | |
285 | } | |
3c6331c2 | 286 | #endif |
10189819 MO |
287 | |
288 | // Malloc hooks. | |
289 | static const int kMaxMallocFreeHooks = 5; | |
290 | struct MallocFreeHook { | |
291 | void (*malloc_hook)(const void *, uptr); | |
292 | void (*free_hook)(const void *); | |
293 | }; | |
294 | ||
295 | static MallocFreeHook MFHooks[kMaxMallocFreeHooks]; | |
296 | ||
297 | void RunMallocHooks(const void *ptr, uptr size) { | |
298 | for (int i = 0; i < kMaxMallocFreeHooks; i++) { | |
299 | auto hook = MFHooks[i].malloc_hook; | |
300 | if (!hook) return; | |
301 | hook(ptr, size); | |
302 | } | |
303 | } | |
304 | ||
305 | void RunFreeHooks(const void *ptr) { | |
306 | for (int i = 0; i < kMaxMallocFreeHooks; i++) { | |
307 | auto hook = MFHooks[i].free_hook; | |
308 | if (!hook) return; | |
309 | hook(ptr); | |
310 | } | |
311 | } | |
312 | ||
313 | static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr), | |
314 | void (*free_hook)(const void *)) { | |
315 | if (!malloc_hook || !free_hook) return 0; | |
316 | for (int i = 0; i < kMaxMallocFreeHooks; i++) { | |
317 | if (MFHooks[i].malloc_hook == nullptr) { | |
318 | MFHooks[i].malloc_hook = malloc_hook; | |
319 | MFHooks[i].free_hook = free_hook; | |
320 | return i + 1; | |
321 | } | |
322 | } | |
323 | return 0; | |
324 | } | |
325 | ||
696d846a MO |
326 | } // namespace __sanitizer |
327 | ||
3ca75cd5 | 328 | using namespace __sanitizer; |
696d846a MO |
329 | |
330 | extern "C" { | |
5d3805fc JJ |
331 | SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary, |
332 | const char *error_summary) { | |
df77f0e4 | 333 | Printf("%s\n", error_summary); |
b4ab7d34 | 334 | } |
696d846a MO |
335 | |
336 | SANITIZER_INTERFACE_ATTRIBUTE | |
eac97531 ML |
337 | int __sanitizer_acquire_crash_state() { |
338 | static atomic_uint8_t in_crash_state = {}; | |
339 | return !atomic_exchange(&in_crash_state, 1, memory_order_relaxed); | |
696d846a | 340 | } |
10189819 MO |
341 | |
342 | SANITIZER_INTERFACE_ATTRIBUTE | |
343 | int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *, | |
344 | uptr), | |
345 | void (*free_hook)(const void *)) { | |
346 | return InstallMallocFreeHooks(malloc_hook, free_hook); | |
347 | } | |
696d846a | 348 | } // extern "C" |