]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc
MFV r225523, r282431:
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / sanitizer_common / sanitizer_common.cc
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
20 namespace __sanitizer {
21
22 const char *SanitizerToolName = "SanitizerTool";
23
24 atomic_uint32_t current_verbosity;
25
26 uptr GetPageSizeCached() {
27   static uptr PageSize;
28   if (!PageSize)
29     PageSize = GetPageSize();
30   return PageSize;
31 }
32
33 StaticSpinMutex report_file_mu;
34 ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
35
36 void RawWrite(const char *buffer) {
37   report_file.Write(buffer, internal_strlen(buffer));
38 }
39
40 void ReportFile::ReopenIfNecessary() {
41   mu->CheckLocked();
42   if (fd == kStdoutFd || fd == kStderrFd) return;
43
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       internal_close(fd);
56   }
57
58   internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
59   uptr openrv = OpenFile(full_path, true);
60   if (internal_iserror(openrv)) {
61     const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
62     internal_write(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
63     internal_write(kStderrFd, full_path, internal_strlen(full_path));
64     Die();
65   }
66   fd = openrv;
67   fd_pid = pid;
68 }
69
70 void ReportFile::SetReportPath(const char *path) {
71   if (!path)
72     return;
73   uptr len = internal_strlen(path);
74   if (len > sizeof(path_prefix) - 100) {
75     Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
76            path[0], path[1], path[2], path[3],
77            path[4], path[5], path[6], path[7]);
78     Die();
79   }
80
81   SpinMutexLock l(mu);
82   if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
83     internal_close(fd);
84   fd = kInvalidFd;
85   if (internal_strcmp(path, "stdout") == 0) {
86     fd = kStdoutFd;
87   } else if (internal_strcmp(path, "stderr") == 0) {
88     fd = kStderrFd;
89   } else {
90     internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
91   }
92 }
93
94 // PID of the tracer task in StopTheWorld. It shares the address space with the
95 // main process, but has a different PID and thus requires special handling.
96 uptr stoptheworld_tracer_pid = 0;
97 // Cached pid of parent process - if the parent process dies, we want to keep
98 // writing to the same log file.
99 uptr stoptheworld_tracer_ppid = 0;
100
101 static DieCallbackType InternalDieCallback, UserDieCallback;
102 void SetDieCallback(DieCallbackType callback) {
103   InternalDieCallback = callback;
104 }
105 void SetUserDieCallback(DieCallbackType callback) {
106   UserDieCallback = callback;
107 }
108
109 DieCallbackType GetDieCallback() {
110   return InternalDieCallback;
111 }
112
113 void NORETURN Die() {
114   if (UserDieCallback)
115     UserDieCallback();
116   if (InternalDieCallback)
117     InternalDieCallback();
118   internal__exit(1);
119 }
120
121 static CheckFailedCallbackType CheckFailedCallback;
122 void SetCheckFailedCallback(CheckFailedCallbackType callback) {
123   CheckFailedCallback = callback;
124 }
125
126 void NORETURN CheckFailed(const char *file, int line, const char *cond,
127                           u64 v1, u64 v2) {
128   if (CheckFailedCallback) {
129     CheckFailedCallback(file, line, cond, v1, v2);
130   }
131   Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
132                                                             v1, v2);
133   Die();
134 }
135
136 uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
137                       uptr max_len, int *errno_p) {
138   uptr PageSize = GetPageSizeCached();
139   uptr kMinFileLen = PageSize;
140   uptr read_len = 0;
141   *buff = 0;
142   *buff_size = 0;
143   // The files we usually open are not seekable, so try different buffer sizes.
144   for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
145     uptr openrv = OpenFile(file_name, /*write*/ false);
146     if (internal_iserror(openrv, errno_p)) return 0;
147     fd_t fd = openrv;
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 = internal_read(fd, *buff + read_len, PageSize);
156       if (internal_iserror(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     internal_close(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>
176 static inline bool CompareLess(const T &a, const T &b) {
177   return a < b;
178 }
179
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.
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
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 *pos = internal_strstr(filepath, strip_path_prefix);
210   if (pos == 0) return filepath;
211   pos += internal_strlen(strip_path_prefix);
212   if (pos[0] == '.' && pos[1] == '/')
213     pos += 2;
214   return pos;
215 }
216
217 const char *StripModuleName(const char *module) {
218   if (module == 0)
219     return 0;
220   if (const char *slash_pos = internal_strrchr(module, '/'))
221     return slash_pos + 1;
222   return module;
223 }
224
225 void ReportErrorSummary(const char *error_message) {
226   if (!common_flags()->print_summary)
227     return;
228   InternalScopedString buff(kMaxSummaryLength);
229   buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
230   __sanitizer_report_error_summary(buff.data());
231 }
232
233 void ReportErrorSummary(const char *error_type, const char *file,
234                         int line, const char *function) {
235   if (!common_flags()->print_summary)
236     return;
237   InternalScopedString buff(kMaxSummaryLength);
238   buff.append("%s %s:%d %s", error_type,
239               file ? StripPathPrefix(file, common_flags()->strip_path_prefix)
240                    : "??",
241               line, function ? function : "??");
242   ReportErrorSummary(buff.data());
243 }
244
245 LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
246   full_name_ = internal_strdup(module_name);
247   base_address_ = base_address;
248   ranges_.clear();
249 }
250
251 void LoadedModule::clear() {
252   InternalFree(full_name_);
253   while (!ranges_.empty()) {
254     AddressRange *r = ranges_.front();
255     ranges_.pop_front();
256     InternalFree(r);
257   }
258 }
259
260 void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
261   void *mem = InternalAlloc(sizeof(AddressRange));
262   AddressRange *r = new(mem) AddressRange(beg, end, executable);
263   ranges_.push_back(r);
264 }
265
266 bool LoadedModule::containsAddress(uptr address) const {
267   for (Iterator iter = ranges(); iter.hasNext();) {
268     const AddressRange *r = iter.next();
269     if (r->beg <= address && address < r->end)
270       return true;
271   }
272   return false;
273 }
274
275 static atomic_uintptr_t g_total_mmaped;
276
277 void IncreaseTotalMmap(uptr size) {
278   if (!common_flags()->mmap_limit_mb) return;
279   uptr total_mmaped =
280       atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
281   // Since for now mmap_limit_mb is not a user-facing flag, just kill
282   // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
283   RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
284 }
285
286 void DecreaseTotalMmap(uptr size) {
287   if (!common_flags()->mmap_limit_mb) return;
288   atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
289 }
290
291 bool TemplateMatch(const char *templ, const char *str) {
292   if (str == 0 || str[0] == 0)
293     return false;
294   bool start = false;
295   if (templ && templ[0] == '^') {
296     start = true;
297     templ++;
298   }
299   bool asterisk = false;
300   while (templ && templ[0]) {
301     if (templ[0] == '*') {
302       templ++;
303       start = false;
304       asterisk = true;
305       continue;
306     }
307     if (templ[0] == '$')
308       return str[0] == 0 || asterisk;
309     if (str[0] == 0)
310       return false;
311     char *tpos = (char*)internal_strchr(templ, '*');
312     char *tpos1 = (char*)internal_strchr(templ, '$');
313     if (tpos == 0 || (tpos1 && tpos1 < tpos))
314       tpos = tpos1;
315     if (tpos != 0)
316       tpos[0] = 0;
317     const char *str0 = str;
318     const char *spos = internal_strstr(str, templ);
319     str = spos + internal_strlen(templ);
320     templ = tpos;
321     if (tpos)
322       tpos[0] = tpos == tpos1 ? '$' : '*';
323     if (spos == 0)
324       return false;
325     if (start && spos != str0)
326       return false;
327     start = false;
328     asterisk = false;
329   }
330   return true;
331 }
332
333 }  // namespace __sanitizer
334
335 using namespace __sanitizer;  // NOLINT
336
337 extern "C" {
338 void __sanitizer_set_report_path(const char *path) {
339   report_file.SetReportPath(path);
340 }
341
342 void __sanitizer_report_error_summary(const char *error_summary) {
343   Printf("%s\n", error_summary);
344 }
345
346 SANITIZER_INTERFACE_ATTRIBUTE
347 void __sanitizer_set_death_callback(void (*callback)(void)) {
348   SetUserDieCallback(callback);
349 }
350 }  // extern "C"