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 internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
61 fd = OpenFile(full_path, WrOnly);
62 if (fd == kInvalidFd) {
63 const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
64 WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
65 WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
66 Die();
67 }
68 fd_pid = pid;
69 }
70
SetReportPath(const char * path)71 void ReportFile::SetReportPath(const char *path) {
72 if (!path)
73 return;
74 uptr len = internal_strlen(path);
75 if (len > sizeof(path_prefix) - 100) {
76 Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
77 path[0], path[1], path[2], path[3],
78 path[4], path[5], path[6], path[7]);
79 Die();
80 }
81
82 SpinMutexLock l(mu);
83 if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
84 CloseFile(fd);
85 fd = kInvalidFd;
86 if (internal_strcmp(path, "stdout") == 0) {
87 fd = kStdoutFd;
88 } else if (internal_strcmp(path, "stderr") == 0) {
89 fd = kStderrFd;
90 } else {
91 internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
92 }
93 }
94
95 // PID of the tracer task in StopTheWorld. It shares the address space with the
96 // main process, but has a different PID and thus requires special handling.
97 uptr stoptheworld_tracer_pid = 0;
98 // Cached pid of parent process - if the parent process dies, we want to keep
99 // writing to the same log file.
100 uptr stoptheworld_tracer_ppid = 0;
101
102 static DieCallbackType InternalDieCallback, UserDieCallback;
SetDieCallback(DieCallbackType callback)103 void SetDieCallback(DieCallbackType callback) {
104 InternalDieCallback = callback;
105 }
SetUserDieCallback(DieCallbackType callback)106 void SetUserDieCallback(DieCallbackType callback) {
107 UserDieCallback = callback;
108 }
109
GetDieCallback()110 DieCallbackType GetDieCallback() {
111 return InternalDieCallback;
112 }
113
Die()114 void NORETURN Die() {
115 if (UserDieCallback)
116 UserDieCallback();
117 if (InternalDieCallback)
118 InternalDieCallback();
119 internal__exit(1);
120 }
121
122 static CheckFailedCallbackType CheckFailedCallback;
SetCheckFailedCallback(CheckFailedCallbackType callback)123 void SetCheckFailedCallback(CheckFailedCallbackType callback) {
124 CheckFailedCallback = callback;
125 }
126
CheckFailed(const char * file,int line,const char * cond,u64 v1,u64 v2)127 void NORETURN CheckFailed(const char *file, int line, const char *cond,
128 u64 v1, u64 v2) {
129 if (CheckFailedCallback) {
130 CheckFailedCallback(file, line, cond, v1, v2);
131 }
132 Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
133 v1, v2);
134 Die();
135 }
136
ReadFileToBuffer(const char * file_name,char ** buff,uptr * buff_size,uptr max_len,error_t * errno_p)137 uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
138 uptr max_len, error_t *errno_p) {
139 uptr PageSize = GetPageSizeCached();
140 uptr kMinFileLen = PageSize;
141 uptr read_len = 0;
142 *buff = 0;
143 *buff_size = 0;
144 // The files we usually open are not seekable, so try different buffer sizes.
145 for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
146 fd_t fd = OpenFile(file_name, RdOnly, errno_p);
147 if (fd == kInvalidFd) return 0;
148 UnmapOrDie(*buff, *buff_size);
149 *buff = (char*)MmapOrDie(size, __func__);
150 *buff_size = size;
151 // Read up to one page at a time.
152 read_len = 0;
153 bool reached_eof = false;
154 while (read_len + PageSize <= size) {
155 uptr just_read;
156 if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) {
157 UnmapOrDie(*buff, *buff_size);
158 return 0;
159 }
160 if (just_read == 0) {
161 reached_eof = true;
162 break;
163 }
164 read_len += just_read;
165 }
166 CloseFile(fd);
167 if (reached_eof) // We've read the whole file.
168 break;
169 }
170 return read_len;
171 }
172
173 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
174
175 template<class T>
CompareLess(const T & a,const T & b)176 static inline bool CompareLess(const T &a, const T &b) {
177 return a < b;
178 }
179
SortArray(uptr * array,uptr size)180 void SortArray(uptr *array, uptr size) {
181 InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
182 }
183
184 // We want to map a chunk of address space aligned to 'alignment'.
185 // We do it by maping a bit more and then unmaping redundant pieces.
186 // We probably can do it with fewer syscalls in some OS-dependent way.
MmapAlignedOrDie(uptr size,uptr alignment,const char * mem_type)187 void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
188 // uptr PageSize = GetPageSizeCached();
189 CHECK(IsPowerOfTwo(size));
190 CHECK(IsPowerOfTwo(alignment));
191 uptr map_size = size + alignment;
192 uptr map_res = (uptr)MmapOrDie(map_size, mem_type);
193 uptr map_end = map_res + map_size;
194 uptr res = map_res;
195 if (res & (alignment - 1)) // Not aligned.
196 res = (map_res + alignment) & ~(alignment - 1);
197 uptr end = res + size;
198 if (res != map_res)
199 UnmapOrDie((void*)map_res, res - map_res);
200 if (end != map_end)
201 UnmapOrDie((void*)end, map_end - end);
202 return (void*)res;
203 }
204
StripPathPrefix(const char * filepath,const char * strip_path_prefix)205 const char *StripPathPrefix(const char *filepath,
206 const char *strip_path_prefix) {
207 if (filepath == 0) return 0;
208 if (strip_path_prefix == 0) return filepath;
209 const char *res = filepath;
210 if (const char *pos = internal_strstr(filepath, strip_path_prefix))
211 res = pos + internal_strlen(strip_path_prefix);
212 if (res[0] == '.' && res[1] == '/')
213 res += 2;
214 return res;
215 }
216
StripModuleName(const char * module)217 const char *StripModuleName(const char *module) {
218 if (module == 0)
219 return 0;
220 if (SANITIZER_WINDOWS) {
221 // On Windows, both slash and backslash are possible.
222 // Pick the one that goes last.
223 if (const char *bslash_pos = internal_strrchr(module, '\\'))
224 return StripModuleName(bslash_pos + 1);
225 }
226 if (const char *slash_pos = internal_strrchr(module, '/')) {
227 return slash_pos + 1;
228 }
229 return module;
230 }
231
ReportErrorSummary(const char * error_message)232 void ReportErrorSummary(const char *error_message) {
233 if (!common_flags()->print_summary)
234 return;
235 InternalScopedString buff(kMaxSummaryLength);
236 buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
237 __sanitizer_report_error_summary(buff.data());
238 }
239
240 #ifndef SANITIZER_GO
ReportErrorSummary(const char * error_type,const AddressInfo & info)241 void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
242 if (!common_flags()->print_summary)
243 return;
244 InternalScopedString buff(kMaxSummaryLength);
245 buff.append("%s ", error_type);
246 RenderFrame(&buff, "%L %F", 0, info, common_flags()->strip_path_prefix);
247 ReportErrorSummary(buff.data());
248 }
249 #endif
250
set(const char * module_name,uptr base_address)251 void LoadedModule::set(const char *module_name, uptr base_address) {
252 clear();
253 full_name_ = internal_strdup(module_name);
254 base_address_ = base_address;
255 }
256
clear()257 void LoadedModule::clear() {
258 InternalFree(full_name_);
259 full_name_ = nullptr;
260 while (!ranges_.empty()) {
261 AddressRange *r = ranges_.front();
262 ranges_.pop_front();
263 InternalFree(r);
264 }
265 }
266
addAddressRange(uptr beg,uptr end,bool executable)267 void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
268 void *mem = InternalAlloc(sizeof(AddressRange));
269 AddressRange *r = new(mem) AddressRange(beg, end, executable);
270 ranges_.push_back(r);
271 }
272
containsAddress(uptr address) const273 bool LoadedModule::containsAddress(uptr address) const {
274 for (Iterator iter = ranges(); iter.hasNext();) {
275 const AddressRange *r = iter.next();
276 if (r->beg <= address && address < r->end)
277 return true;
278 }
279 return false;
280 }
281
282 static atomic_uintptr_t g_total_mmaped;
283
IncreaseTotalMmap(uptr size)284 void IncreaseTotalMmap(uptr size) {
285 if (!common_flags()->mmap_limit_mb) return;
286 uptr total_mmaped =
287 atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
288 // Since for now mmap_limit_mb is not a user-facing flag, just kill
289 // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
290 RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
291 }
292
DecreaseTotalMmap(uptr size)293 void DecreaseTotalMmap(uptr size) {
294 if (!common_flags()->mmap_limit_mb) return;
295 atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
296 }
297
TemplateMatch(const char * templ,const char * str)298 bool TemplateMatch(const char *templ, const char *str) {
299 if (str == 0 || str[0] == 0)
300 return false;
301 bool start = false;
302 if (templ && templ[0] == '^') {
303 start = true;
304 templ++;
305 }
306 bool asterisk = false;
307 while (templ && templ[0]) {
308 if (templ[0] == '*') {
309 templ++;
310 start = false;
311 asterisk = true;
312 continue;
313 }
314 if (templ[0] == '$')
315 return str[0] == 0 || asterisk;
316 if (str[0] == 0)
317 return false;
318 char *tpos = (char*)internal_strchr(templ, '*');
319 char *tpos1 = (char*)internal_strchr(templ, '$');
320 if (tpos == 0 || (tpos1 && tpos1 < tpos))
321 tpos = tpos1;
322 if (tpos != 0)
323 tpos[0] = 0;
324 const char *str0 = str;
325 const char *spos = internal_strstr(str, templ);
326 str = spos + internal_strlen(templ);
327 templ = tpos;
328 if (tpos)
329 tpos[0] = tpos == tpos1 ? '$' : '*';
330 if (spos == 0)
331 return false;
332 if (start && spos != str0)
333 return false;
334 start = false;
335 asterisk = false;
336 }
337 return true;
338 }
339
340 } // namespace __sanitizer
341
342 using namespace __sanitizer; // NOLINT
343
344 extern "C" {
__sanitizer_set_report_path(const char * path)345 void __sanitizer_set_report_path(const char *path) {
346 report_file.SetReportPath(path);
347 }
348
__sanitizer_report_error_summary(const char * error_summary)349 void __sanitizer_report_error_summary(const char *error_summary) {
350 Printf("%s\n", error_summary);
351 }
352
353 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_set_death_callback(void (* callback)(void))354 void __sanitizer_set_death_callback(void (*callback)(void)) {
355 SetUserDieCallback(callback);
356 }
357 } // extern "C"
358