* fi.po: Update.
[official-gcc.git] / libsanitizer / sanitizer_common / sanitizer_common.cc
blob4529e63eba96f1512d70f55500f4a21e8ceaf88a
1 //===-- sanitizer_common.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 shared between AddressSanitizer and ThreadSanitizer
9 // run-time libraries.
10 //===----------------------------------------------------------------------===//
12 #include "sanitizer_common.h"
13 #include "sanitizer_allocator_internal.h"
14 #include "sanitizer_flags.h"
15 #include "sanitizer_libc.h"
16 #include "sanitizer_placement_new.h"
17 #include "sanitizer_stacktrace_printer.h"
18 #include "sanitizer_symbolizer.h"
20 namespace __sanitizer {
22 const char *SanitizerToolName = "SanitizerTool";
24 atomic_uint32_t current_verbosity;
26 uptr GetPageSizeCached() {
27 static uptr PageSize;
28 if (!PageSize)
29 PageSize = GetPageSize();
30 return PageSize;
33 StaticSpinMutex report_file_mu;
34 ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
36 void RawWrite(const char *buffer) {
37 report_file.Write(buffer, internal_strlen(buffer));
40 void ReportFile::ReopenIfNecessary() {
41 mu->CheckLocked();
42 if (fd == kStdoutFd || fd == kStderrFd) return;
44 uptr pid = internal_getpid();
45 // If in tracer, use the parent's file.
46 if (pid == stoptheworld_tracer_pid)
47 pid = stoptheworld_tracer_ppid;
48 if (fd != kInvalidFd) {
49 // If the report file is already opened by the current process,
50 // do nothing. Otherwise the report file was opened by the parent
51 // process, close it now.
52 if (fd_pid == pid)
53 return;
54 else
55 CloseFile(fd);
58 const char *exe_name = GetProcessName();
59 if (common_flags()->log_exe_name && exe_name) {
60 internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
61 exe_name, pid);
62 } else {
63 internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
65 fd = OpenFile(full_path, WrOnly);
66 if (fd == kInvalidFd) {
67 const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
68 WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
69 WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
70 Die();
72 fd_pid = pid;
75 void ReportFile::SetReportPath(const char *path) {
76 if (!path)
77 return;
78 uptr len = internal_strlen(path);
79 if (len > sizeof(path_prefix) - 100) {
80 Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
81 path[0], path[1], path[2], path[3],
82 path[4], path[5], path[6], path[7]);
83 Die();
86 SpinMutexLock l(mu);
87 if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
88 CloseFile(fd);
89 fd = kInvalidFd;
90 if (internal_strcmp(path, "stdout") == 0) {
91 fd = kStdoutFd;
92 } else if (internal_strcmp(path, "stderr") == 0) {
93 fd = kStderrFd;
94 } else {
95 internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
99 // PID of the tracer task in StopTheWorld. It shares the address space with the
100 // main process, but has a different PID and thus requires special handling.
101 uptr stoptheworld_tracer_pid = 0;
102 // Cached pid of parent process - if the parent process dies, we want to keep
103 // writing to the same log file.
104 uptr stoptheworld_tracer_ppid = 0;
106 static const int kMaxNumOfInternalDieCallbacks = 5;
107 static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
109 bool AddDieCallback(DieCallbackType callback) {
110 for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
111 if (InternalDieCallbacks[i] == nullptr) {
112 InternalDieCallbacks[i] = callback;
113 return true;
116 return false;
119 bool RemoveDieCallback(DieCallbackType callback) {
120 for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
121 if (InternalDieCallbacks[i] == callback) {
122 internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
123 sizeof(InternalDieCallbacks[0]) *
124 (kMaxNumOfInternalDieCallbacks - i - 1));
125 InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
126 return true;
129 return false;
132 static DieCallbackType UserDieCallback;
133 void SetUserDieCallback(DieCallbackType callback) {
134 UserDieCallback = callback;
137 void NORETURN Die() {
138 if (UserDieCallback)
139 UserDieCallback();
140 for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
141 if (InternalDieCallbacks[i])
142 InternalDieCallbacks[i]();
144 if (common_flags()->abort_on_error)
145 Abort();
146 internal__exit(common_flags()->exitcode);
149 static CheckFailedCallbackType CheckFailedCallback;
150 void SetCheckFailedCallback(CheckFailedCallbackType callback) {
151 CheckFailedCallback = callback;
154 void NORETURN CheckFailed(const char *file, int line, const char *cond,
155 u64 v1, u64 v2) {
156 if (CheckFailedCallback) {
157 CheckFailedCallback(file, line, cond, v1, v2);
159 Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
160 v1, v2);
161 Die();
164 void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
165 const char *mmap_type, error_t err) {
166 static int recursion_count;
167 if (recursion_count) {
168 // The Report() and CHECK calls below may call mmap recursively and fail.
169 // If we went into recursion, just die.
170 RawWrite("ERROR: Failed to mmap\n");
171 Die();
173 recursion_count++;
174 Report("ERROR: %s failed to "
175 "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
176 SanitizerToolName, mmap_type, size, size, mem_type, err);
177 #ifndef SANITIZER_GO
178 DumpProcessMap();
179 #endif
180 UNREACHABLE("unable to mmap");
183 bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
184 uptr *read_len, uptr max_len, error_t *errno_p) {
185 uptr PageSize = GetPageSizeCached();
186 uptr kMinFileLen = PageSize;
187 *buff = nullptr;
188 *buff_size = 0;
189 *read_len = 0;
190 // The files we usually open are not seekable, so try different buffer sizes.
191 for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
192 fd_t fd = OpenFile(file_name, RdOnly, errno_p);
193 if (fd == kInvalidFd) return false;
194 UnmapOrDie(*buff, *buff_size);
195 *buff = (char*)MmapOrDie(size, __func__);
196 *buff_size = size;
197 *read_len = 0;
198 // Read up to one page at a time.
199 bool reached_eof = false;
200 while (*read_len + PageSize <= size) {
201 uptr just_read;
202 if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) {
203 UnmapOrDie(*buff, *buff_size);
204 return false;
206 if (just_read == 0) {
207 reached_eof = true;
208 break;
210 *read_len += just_read;
212 CloseFile(fd);
213 if (reached_eof) // We've read the whole file.
214 break;
216 return true;
219 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
221 template<class T>
222 static inline bool CompareLess(const T &a, const T &b) {
223 return a < b;
226 void SortArray(uptr *array, uptr size) {
227 InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
230 // We want to map a chunk of address space aligned to 'alignment'.
231 // We do it by maping a bit more and then unmaping redundant pieces.
232 // We probably can do it with fewer syscalls in some OS-dependent way.
233 void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
234 // uptr PageSize = GetPageSizeCached();
235 CHECK(IsPowerOfTwo(size));
236 CHECK(IsPowerOfTwo(alignment));
237 uptr map_size = size + alignment;
238 uptr map_res = (uptr)MmapOrDie(map_size, mem_type);
239 uptr map_end = map_res + map_size;
240 uptr res = map_res;
241 if (res & (alignment - 1)) // Not aligned.
242 res = (map_res + alignment) & ~(alignment - 1);
243 uptr end = res + size;
244 if (res != map_res)
245 UnmapOrDie((void*)map_res, res - map_res);
246 if (end != map_end)
247 UnmapOrDie((void*)end, map_end - end);
248 return (void*)res;
251 const char *StripPathPrefix(const char *filepath,
252 const char *strip_path_prefix) {
253 if (!filepath) return nullptr;
254 if (!strip_path_prefix) return filepath;
255 const char *res = filepath;
256 if (const char *pos = internal_strstr(filepath, strip_path_prefix))
257 res = pos + internal_strlen(strip_path_prefix);
258 if (res[0] == '.' && res[1] == '/')
259 res += 2;
260 return res;
263 const char *StripModuleName(const char *module) {
264 if (!module)
265 return nullptr;
266 if (SANITIZER_WINDOWS) {
267 // On Windows, both slash and backslash are possible.
268 // Pick the one that goes last.
269 if (const char *bslash_pos = internal_strrchr(module, '\\'))
270 return StripModuleName(bslash_pos + 1);
272 if (const char *slash_pos = internal_strrchr(module, '/')) {
273 return slash_pos + 1;
275 return module;
278 void ReportErrorSummary(const char *error_message) {
279 if (!common_flags()->print_summary)
280 return;
281 InternalScopedString buff(kMaxSummaryLength);
282 buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
283 __sanitizer_report_error_summary(buff.data());
286 #ifndef SANITIZER_GO
287 void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
288 if (!common_flags()->print_summary)
289 return;
290 InternalScopedString buff(kMaxSummaryLength);
291 buff.append("%s ", error_type);
292 RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
293 common_flags()->strip_path_prefix);
294 ReportErrorSummary(buff.data());
296 #endif
298 void LoadedModule::set(const char *module_name, uptr base_address) {
299 clear();
300 full_name_ = internal_strdup(module_name);
301 base_address_ = base_address;
304 void LoadedModule::clear() {
305 InternalFree(full_name_);
306 full_name_ = nullptr;
307 while (!ranges_.empty()) {
308 AddressRange *r = ranges_.front();
309 ranges_.pop_front();
310 InternalFree(r);
314 void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
315 void *mem = InternalAlloc(sizeof(AddressRange));
316 AddressRange *r = new(mem) AddressRange(beg, end, executable);
317 ranges_.push_back(r);
320 bool LoadedModule::containsAddress(uptr address) const {
321 for (Iterator iter = ranges(); iter.hasNext();) {
322 const AddressRange *r = iter.next();
323 if (r->beg <= address && address < r->end)
324 return true;
326 return false;
329 static atomic_uintptr_t g_total_mmaped;
331 void IncreaseTotalMmap(uptr size) {
332 if (!common_flags()->mmap_limit_mb) return;
333 uptr total_mmaped =
334 atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
335 // Since for now mmap_limit_mb is not a user-facing flag, just kill
336 // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
337 RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
340 void DecreaseTotalMmap(uptr size) {
341 if (!common_flags()->mmap_limit_mb) return;
342 atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
345 bool TemplateMatch(const char *templ, const char *str) {
346 if ((!str) || str[0] == 0)
347 return false;
348 bool start = false;
349 if (templ && templ[0] == '^') {
350 start = true;
351 templ++;
353 bool asterisk = false;
354 while (templ && templ[0]) {
355 if (templ[0] == '*') {
356 templ++;
357 start = false;
358 asterisk = true;
359 continue;
361 if (templ[0] == '$')
362 return str[0] == 0 || asterisk;
363 if (str[0] == 0)
364 return false;
365 char *tpos = (char*)internal_strchr(templ, '*');
366 char *tpos1 = (char*)internal_strchr(templ, '$');
367 if ((!tpos) || (tpos1 && tpos1 < tpos))
368 tpos = tpos1;
369 if (tpos)
370 tpos[0] = 0;
371 const char *str0 = str;
372 const char *spos = internal_strstr(str, templ);
373 str = spos + internal_strlen(templ);
374 templ = tpos;
375 if (tpos)
376 tpos[0] = tpos == tpos1 ? '$' : '*';
377 if (!spos)
378 return false;
379 if (start && spos != str0)
380 return false;
381 start = false;
382 asterisk = false;
384 return true;
387 static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
389 char *FindPathToBinary(const char *name) {
390 const char *path = GetEnv("PATH");
391 if (!path)
392 return nullptr;
393 uptr name_len = internal_strlen(name);
394 InternalScopedBuffer<char> buffer(kMaxPathLength);
395 const char *beg = path;
396 while (true) {
397 const char *end = internal_strchrnul(beg, kPathSeparator);
398 uptr prefix_len = end - beg;
399 if (prefix_len + name_len + 2 <= kMaxPathLength) {
400 internal_memcpy(buffer.data(), beg, prefix_len);
401 buffer[prefix_len] = '/';
402 internal_memcpy(&buffer[prefix_len + 1], name, name_len);
403 buffer[prefix_len + 1 + name_len] = '\0';
404 if (FileExists(buffer.data()))
405 return internal_strdup(buffer.data());
407 if (*end == '\0') break;
408 beg = end + 1;
410 return nullptr;
413 static char binary_name_cache_str[kMaxPathLength];
414 static char process_name_cache_str[kMaxPathLength];
416 const char *GetProcessName() {
417 return process_name_cache_str;
420 static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
421 ReadLongProcessName(buf, buf_len);
422 char *s = const_cast<char *>(StripModuleName(buf));
423 uptr len = internal_strlen(s);
424 if (s != buf) {
425 internal_memmove(buf, s, len);
426 buf[len] = '\0';
428 return len;
431 void UpdateProcessName() {
432 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
435 // Call once to make sure that binary_name_cache_str is initialized
436 void CacheBinaryName() {
437 if (binary_name_cache_str[0] != '\0')
438 return;
439 ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
440 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
443 uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
444 CacheBinaryName();
445 uptr name_len = internal_strlen(binary_name_cache_str);
446 name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
447 if (buf_len == 0)
448 return 0;
449 internal_memcpy(buf, binary_name_cache_str, name_len);
450 buf[name_len] = '\0';
451 return name_len;
454 } // namespace __sanitizer
456 using namespace __sanitizer; // NOLINT
458 extern "C" {
459 void __sanitizer_set_report_path(const char *path) {
460 report_file.SetReportPath(path);
463 void __sanitizer_report_error_summary(const char *error_summary) {
464 Printf("%s\n", error_summary);
467 SANITIZER_INTERFACE_ATTRIBUTE
468 void __sanitizer_set_death_callback(void (*callback)(void)) {
469 SetUserDieCallback(callback);
471 } // extern "C"