1 //===-- sanitizer_common.cc -----------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is shared between AddressSanitizer and ThreadSanitizer
11 // run-time libraries.
12 //===----------------------------------------------------------------------===//
13
14 #include "sanitizer_common.h"
15 #include "sanitizer_allocator_internal.h"
16 #include "sanitizer_flags.h"
17 #include "sanitizer_libc.h"
18 #include "sanitizer_placement_new.h"
19 #include "sanitizer_stacktrace_printer.h"
20 #include "sanitizer_symbolizer.h"
21
22 namespace __sanitizer {
23
24 const char *SanitizerToolName = "SanitizerTool";
25
26 atomic_uint32_t current_verbosity;
27
GetPageSizeCached()28 uptr GetPageSizeCached() {
29 static uptr PageSize;
30 if (!PageSize)
31 PageSize = GetPageSize();
32 return PageSize;
33 }
34
35 StaticSpinMutex report_file_mu;
36 ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
37
RawWrite(const char * buffer)38 void RawWrite(const char *buffer) {
39 report_file.Write(buffer, internal_strlen(buffer));
40 }
41
ReopenIfNecessary()42 void ReportFile::ReopenIfNecessary() {
43 mu->CheckLocked();
44 if (fd == kStdoutFd || fd == kStderrFd) return;
45
46 uptr pid = internal_getpid();
47 // If in tracer, use the parent's file.
48 if (pid == stoptheworld_tracer_pid)
49 pid = stoptheworld_tracer_ppid;
50 if (fd != kInvalidFd) {
51 // If the report file is already opened by the current process,
52 // do nothing. Otherwise the report file was opened by the parent
53 // process, close it now.
54 if (fd_pid == pid)
55 return;
56 else
57 CloseFile(fd);
58 }
59
60 const char *exe_name = GetBinaryBasename();
61 if (common_flags()->log_exe_name && exe_name) {
62 internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
63 exe_name, pid);
64 } else {
65 internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
66 }
67 fd = OpenFile(full_path, WrOnly);
68 if (fd == kInvalidFd) {
69 const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
70 WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
71 WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
72 Die();
73 }
74 fd_pid = pid;
75 }
76
SetReportPath(const char * path)77 void ReportFile::SetReportPath(const char *path) {
78 if (!path)
79 return;
80 uptr len = internal_strlen(path);
81 if (len > sizeof(path_prefix) - 100) {
82 Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
83 path[0], path[1], path[2], path[3],
84 path[4], path[5], path[6], path[7]);
85 Die();
86 }
87
88 SpinMutexLock l(mu);
89 if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
90 CloseFile(fd);
91 fd = kInvalidFd;
92 if (internal_strcmp(path, "stdout") == 0) {
93 fd = kStdoutFd;
94 } else if (internal_strcmp(path, "stderr") == 0) {
95 fd = kStderrFd;
96 } else {
97 internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
98 }
99 }
100
101 // PID of the tracer task in StopTheWorld. It shares the address space with the
102 // main process, but has a different PID and thus requires special handling.
103 uptr stoptheworld_tracer_pid = 0;
104 // Cached pid of parent process - if the parent process dies, we want to keep
105 // writing to the same log file.
106 uptr stoptheworld_tracer_ppid = 0;
107
108 static DieCallbackType InternalDieCallback, UserDieCallback;
SetDieCallback(DieCallbackType callback)109 void SetDieCallback(DieCallbackType callback) {
110 InternalDieCallback = callback;
111 }
SetUserDieCallback(DieCallbackType callback)112 void SetUserDieCallback(DieCallbackType callback) {
113 UserDieCallback = callback;
114 }
115
GetDieCallback()116 DieCallbackType GetDieCallback() {
117 return InternalDieCallback;
118 }
119
Die()120 void NORETURN Die() {
121 if (UserDieCallback)
122 UserDieCallback();
123 if (InternalDieCallback)
124 InternalDieCallback();
125 internal__exit(1);
126 }
127
128 static CheckFailedCallbackType CheckFailedCallback;
SetCheckFailedCallback(CheckFailedCallbackType callback)129 void SetCheckFailedCallback(CheckFailedCallbackType callback) {
130 CheckFailedCallback = callback;
131 }
132
CheckFailed(const char * file,int line,const char * cond,u64 v1,u64 v2)133 void NORETURN CheckFailed(const char *file, int line, const char *cond,
134 u64 v1, u64 v2) {
135 if (CheckFailedCallback) {
136 CheckFailedCallback(file, line, cond, v1, v2);
137 }
138 Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
139 v1, v2);
140 Die();
141 }
142
ReadFileToBuffer(const char * file_name,char ** buff,uptr * buff_size,uptr max_len,error_t * errno_p)143 uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
144 uptr max_len, error_t *errno_p) {
145 uptr PageSize = GetPageSizeCached();
146 uptr kMinFileLen = PageSize;
147 uptr read_len = 0;
148 *buff = 0;
149 *buff_size = 0;
150 // The files we usually open are not seekable, so try different buffer sizes.
151 for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
152 fd_t fd = OpenFile(file_name, RdOnly, errno_p);
153 if (fd == kInvalidFd) return 0;
154 UnmapOrDie(*buff, *buff_size);
155 *buff = (char*)MmapOrDie(size, __func__);
156 *buff_size = size;
157 // Read up to one page at a time.
158 read_len = 0;
159 bool reached_eof = false;
160 while (read_len + PageSize <= size) {
161 uptr just_read;
162 if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) {
163 UnmapOrDie(*buff, *buff_size);
164 return 0;
165 }
166 if (just_read == 0) {
167 reached_eof = true;
168 break;
169 }
170 read_len += just_read;
171 }
172 CloseFile(fd);
173 if (reached_eof) // We've read the whole file.
174 break;
175 }
176 return read_len;
177 }
178
179 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
180
181 template<class T>
CompareLess(const T & a,const T & b)182 static inline bool CompareLess(const T &a, const T &b) {
183 return a < b;
184 }
185
SortArray(uptr * array,uptr size)186 void SortArray(uptr *array, uptr size) {
187 InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
188 }
189
190 // We want to map a chunk of address space aligned to 'alignment'.
191 // We do it by maping a bit more and then unmaping redundant pieces.
192 // We probably can do it with fewer syscalls in some OS-dependent way.
MmapAlignedOrDie(uptr size,uptr alignment,const char * mem_type)193 void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
194 // uptr PageSize = GetPageSizeCached();
195 CHECK(IsPowerOfTwo(size));
196 CHECK(IsPowerOfTwo(alignment));
197 uptr map_size = size + alignment;
198 uptr map_res = (uptr)MmapOrDie(map_size, mem_type);
199 uptr map_end = map_res + map_size;
200 uptr res = map_res;
201 if (res & (alignment - 1)) // Not aligned.
202 res = (map_res + alignment) & ~(alignment - 1);
203 uptr end = res + size;
204 if (res != map_res)
205 UnmapOrDie((void*)map_res, res - map_res);
206 if (end != map_end)
207 UnmapOrDie((void*)end, map_end - end);
208 return (void*)res;
209 }
210
StripPathPrefix(const char * filepath,const char * strip_path_prefix)211 const char *StripPathPrefix(const char *filepath,
212 const char *strip_path_prefix) {
213 if (filepath == 0) return 0;
214 if (strip_path_prefix == 0) return filepath;
215 const char *res = filepath;
216 if (const char *pos = internal_strstr(filepath, strip_path_prefix))
217 res = pos + internal_strlen(strip_path_prefix);
218 if (res[0] == '.' && res[1] == '/')
219 res += 2;
220 return res;
221 }
222
StripModuleName(const char * module)223 const char *StripModuleName(const char *module) {
224 if (module == 0)
225 return 0;
226 if (SANITIZER_WINDOWS) {
227 // On Windows, both slash and backslash are possible.
228 // Pick the one that goes last.
229 if (const char *bslash_pos = internal_strrchr(module, '\\'))
230 return StripModuleName(bslash_pos + 1);
231 }
232 if (const char *slash_pos = internal_strrchr(module, '/')) {
233 return slash_pos + 1;
234 }
235 return module;
236 }
237
ReportErrorSummary(const char * error_message)238 void ReportErrorSummary(const char *error_message) {
239 if (!common_flags()->print_summary)
240 return;
241 InternalScopedString buff(kMaxSummaryLength);
242 buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
243 __sanitizer_report_error_summary(buff.data());
244 }
245
246 #ifndef SANITIZER_GO
ReportErrorSummary(const char * error_type,const AddressInfo & info)247 void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
248 if (!common_flags()->print_summary)
249 return;
250 InternalScopedString buff(kMaxSummaryLength);
251 buff.append("%s ", error_type);
252 RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
253 common_flags()->strip_path_prefix);
254 ReportErrorSummary(buff.data());
255 }
256 #endif
257
set(const char * module_name,uptr base_address)258 void LoadedModule::set(const char *module_name, uptr base_address) {
259 clear();
260 full_name_ = internal_strdup(module_name);
261 base_address_ = base_address;
262 }
263
clear()264 void LoadedModule::clear() {
265 InternalFree(full_name_);
266 full_name_ = nullptr;
267 while (!ranges_.empty()) {
268 AddressRange *r = ranges_.front();
269 ranges_.pop_front();
270 InternalFree(r);
271 }
272 }
273
addAddressRange(uptr beg,uptr end,bool executable)274 void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
275 void *mem = InternalAlloc(sizeof(AddressRange));
276 AddressRange *r = new(mem) AddressRange(beg, end, executable);
277 ranges_.push_back(r);
278 }
279
containsAddress(uptr address) const280 bool LoadedModule::containsAddress(uptr address) const {
281 for (Iterator iter = ranges(); iter.hasNext();) {
282 const AddressRange *r = iter.next();
283 if (r->beg <= address && address < r->end)
284 return true;
285 }
286 return false;
287 }
288
289 static atomic_uintptr_t g_total_mmaped;
290
IncreaseTotalMmap(uptr size)291 void IncreaseTotalMmap(uptr size) {
292 if (!common_flags()->mmap_limit_mb) return;
293 uptr total_mmaped =
294 atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
295 // Since for now mmap_limit_mb is not a user-facing flag, just kill
296 // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
297 RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
298 }
299
DecreaseTotalMmap(uptr size)300 void DecreaseTotalMmap(uptr size) {
301 if (!common_flags()->mmap_limit_mb) return;
302 atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
303 }
304
TemplateMatch(const char * templ,const char * str)305 bool TemplateMatch(const char *templ, const char *str) {
306 if (str == 0 || str[0] == 0)
307 return false;
308 bool start = false;
309 if (templ && templ[0] == '^') {
310 start = true;
311 templ++;
312 }
313 bool asterisk = false;
314 while (templ && templ[0]) {
315 if (templ[0] == '*') {
316 templ++;
317 start = false;
318 asterisk = true;
319 continue;
320 }
321 if (templ[0] == '$')
322 return str[0] == 0 || asterisk;
323 if (str[0] == 0)
324 return false;
325 char *tpos = (char*)internal_strchr(templ, '*');
326 char *tpos1 = (char*)internal_strchr(templ, '$');
327 if (tpos == 0 || (tpos1 && tpos1 < tpos))
328 tpos = tpos1;
329 if (tpos != 0)
330 tpos[0] = 0;
331 const char *str0 = str;
332 const char *spos = internal_strstr(str, templ);
333 str = spos + internal_strlen(templ);
334 templ = tpos;
335 if (tpos)
336 tpos[0] = tpos == tpos1 ? '$' : '*';
337 if (spos == 0)
338 return false;
339 if (start && spos != str0)
340 return false;
341 start = false;
342 asterisk = false;
343 }
344 return true;
345 }
346
347 static char binary_name_cache_str[kMaxPathLength];
348 static const char *binary_basename_cache_str;
349
GetBinaryBasename()350 const char *GetBinaryBasename() {
351 return binary_basename_cache_str;
352 }
353
354 // Call once to make sure that binary_name_cache_str is initialized
CacheBinaryName()355 void CacheBinaryName() {
356 if (binary_name_cache_str[0] != '\0')
357 return;
358 ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
359 binary_basename_cache_str = StripModuleName(binary_name_cache_str);
360 }
361
ReadBinaryNameCached(char * buf,uptr buf_len)362 uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
363 CacheBinaryName();
364 uptr name_len = internal_strlen(binary_name_cache_str);
365 name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
366 if (buf_len == 0)
367 return 0;
368 internal_memcpy(buf, binary_name_cache_str, name_len);
369 buf[name_len] = '\0';
370 return name_len;
371 }
372
373 } // namespace __sanitizer
374
375 using namespace __sanitizer; // NOLINT
376
377 extern "C" {
__sanitizer_set_report_path(const char * path)378 void __sanitizer_set_report_path(const char *path) {
379 report_file.SetReportPath(path);
380 }
381
__sanitizer_report_error_summary(const char * error_summary)382 void __sanitizer_report_error_summary(const char *error_summary) {
383 Printf("%s\n", error_summary);
384 }
385
386 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_set_death_callback(void (* callback)(void))387 void __sanitizer_set_death_callback(void (*callback)(void)) {
388 SetUserDieCallback(callback);
389 }
390 } // extern "C"
391