xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- sanitizer_common_libcdep.cpp --------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is shared between AddressSanitizer and ThreadSanitizer
103cab2bb3Spatrick // run-time libraries.
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick 
13*810390e3Srobert #include "sanitizer_allocator.h"
143cab2bb3Spatrick #include "sanitizer_allocator_interface.h"
153cab2bb3Spatrick #include "sanitizer_common.h"
163cab2bb3Spatrick #include "sanitizer_flags.h"
17*810390e3Srobert #include "sanitizer_interface_internal.h"
183cab2bb3Spatrick #include "sanitizer_procmaps.h"
19*810390e3Srobert #include "sanitizer_stackdepot.h"
203cab2bb3Spatrick 
213cab2bb3Spatrick namespace __sanitizer {
223cab2bb3Spatrick 
233cab2bb3Spatrick #if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO
243cab2bb3Spatrick // Weak default implementation for when sanitizer_stackdepot is not linked in.
StackDepotGetStats()25*810390e3Srobert SANITIZER_WEAK_ATTRIBUTE StackDepotStats StackDepotGetStats() { return {}; }
263cab2bb3Spatrick 
BackgroundThread(void * arg)271f9cb04fSpatrick void *BackgroundThread(void *arg) {
28*810390e3Srobert   VPrintf(1, "%s: Started BackgroundThread\n", SanitizerToolName);
293cab2bb3Spatrick   const uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
303cab2bb3Spatrick   const uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
313cab2bb3Spatrick   const bool heap_profile = common_flags()->heap_profile;
323cab2bb3Spatrick   uptr prev_reported_rss = 0;
333cab2bb3Spatrick   uptr prev_reported_stack_depot_size = 0;
343cab2bb3Spatrick   bool reached_soft_rss_limit = false;
353cab2bb3Spatrick   uptr rss_during_last_reported_profile = 0;
363cab2bb3Spatrick   while (true) {
373cab2bb3Spatrick     SleepForMillis(100);
383cab2bb3Spatrick     const uptr current_rss_mb = GetRSS() >> 20;
393cab2bb3Spatrick     if (Verbosity()) {
403cab2bb3Spatrick       // If RSS has grown 10% since last time, print some information.
413cab2bb3Spatrick       if (prev_reported_rss * 11 / 10 < current_rss_mb) {
423cab2bb3Spatrick         Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
433cab2bb3Spatrick         prev_reported_rss = current_rss_mb;
443cab2bb3Spatrick       }
453cab2bb3Spatrick       // If stack depot has grown 10% since last time, print it too.
46*810390e3Srobert       StackDepotStats stack_depot_stats = StackDepotGetStats();
473cab2bb3Spatrick       if (prev_reported_stack_depot_size * 11 / 10 <
48*810390e3Srobert           stack_depot_stats.allocated) {
49*810390e3Srobert         Printf("%s: StackDepot: %zd ids; %zdM allocated\n", SanitizerToolName,
50*810390e3Srobert                stack_depot_stats.n_uniq_ids, stack_depot_stats.allocated >> 20);
51*810390e3Srobert         prev_reported_stack_depot_size = stack_depot_stats.allocated;
523cab2bb3Spatrick       }
533cab2bb3Spatrick     }
543cab2bb3Spatrick     // Check RSS against the limit.
553cab2bb3Spatrick     if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
563cab2bb3Spatrick       Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
573cab2bb3Spatrick              SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
583cab2bb3Spatrick       DumpProcessMap();
593cab2bb3Spatrick       Die();
603cab2bb3Spatrick     }
613cab2bb3Spatrick     if (soft_rss_limit_mb) {
623cab2bb3Spatrick       if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
633cab2bb3Spatrick         reached_soft_rss_limit = true;
643cab2bb3Spatrick         Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
653cab2bb3Spatrick                SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
66*810390e3Srobert         SetRssLimitExceeded(true);
673cab2bb3Spatrick       } else if (soft_rss_limit_mb >= current_rss_mb &&
683cab2bb3Spatrick                  reached_soft_rss_limit) {
693cab2bb3Spatrick         reached_soft_rss_limit = false;
70*810390e3Srobert         SetRssLimitExceeded(false);
713cab2bb3Spatrick       }
723cab2bb3Spatrick     }
733cab2bb3Spatrick     if (heap_profile &&
743cab2bb3Spatrick         current_rss_mb > rss_during_last_reported_profile * 1.1) {
753cab2bb3Spatrick       Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb);
763cab2bb3Spatrick       __sanitizer_print_memory_profile(90, 20);
773cab2bb3Spatrick       rss_during_last_reported_profile = current_rss_mb;
783cab2bb3Spatrick     }
793cab2bb3Spatrick   }
803cab2bb3Spatrick }
81*810390e3Srobert 
MaybeStartBackgroudThread()82*810390e3Srobert void MaybeStartBackgroudThread() {
83*810390e3Srobert   // Need to implement/test on other platforms.
84*810390e3Srobert   // Start the background thread if one of the rss limits is given.
85*810390e3Srobert   if (!common_flags()->hard_rss_limit_mb &&
86*810390e3Srobert       !common_flags()->soft_rss_limit_mb &&
87*810390e3Srobert       !common_flags()->heap_profile) return;
88*810390e3Srobert   if (!&real_pthread_create) {
89*810390e3Srobert     VPrintf(1, "%s: real_pthread_create undefined\n", SanitizerToolName);
90*810390e3Srobert     return;  // Can't spawn the thread anyway.
91*810390e3Srobert   }
92*810390e3Srobert 
93*810390e3Srobert   static bool started = false;
94*810390e3Srobert   if (!started) {
95*810390e3Srobert     started = true;
96*810390e3Srobert     internal_start_thread(BackgroundThread, nullptr);
97*810390e3Srobert   }
98*810390e3Srobert }
99*810390e3Srobert 
100*810390e3Srobert #  if !SANITIZER_START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
101*810390e3Srobert #    ifdef __clang__
102*810390e3Srobert #    pragma clang diagnostic push
103*810390e3Srobert // We avoid global-constructors to be sure that globals are ready when
104*810390e3Srobert // sanitizers need them. This can happend before global constructors executed.
105*810390e3Srobert // Here we don't mind if thread is started on later stages.
106*810390e3Srobert #    pragma clang diagnostic ignored "-Wglobal-constructors"
107*810390e3Srobert #    endif
108*810390e3Srobert static struct BackgroudThreadStarted {
BackgroudThreadStarted__sanitizer::BackgroudThreadStarted109*810390e3Srobert   BackgroudThreadStarted() { MaybeStartBackgroudThread(); }
110*810390e3Srobert } background_thread_strarter UNUSED;
111*810390e3Srobert #    ifdef __clang__
112*810390e3Srobert #    pragma clang diagnostic pop
113*810390e3Srobert #    endif
114*810390e3Srobert #  endif
115*810390e3Srobert #else
116*810390e3Srobert void MaybeStartBackgroudThread() {}
1173cab2bb3Spatrick #endif
1183cab2bb3Spatrick 
WriteToSyslog(const char * msg)1193cab2bb3Spatrick void WriteToSyslog(const char *msg) {
120d89ec533Spatrick   InternalScopedString msg_copy;
1213cab2bb3Spatrick   msg_copy.append("%s", msg);
122d89ec533Spatrick   const char *p = msg_copy.data();
1233cab2bb3Spatrick 
1243cab2bb3Spatrick   // Print one line at a time.
1253cab2bb3Spatrick   // syslog, at least on Android, has an implicit message length limit.
126d89ec533Spatrick   while (char* q = internal_strchr(p, '\n')) {
1273cab2bb3Spatrick     *q = '\0';
1283cab2bb3Spatrick     WriteOneLineToSyslog(p);
1293cab2bb3Spatrick     p = q + 1;
1303cab2bb3Spatrick   }
1313cab2bb3Spatrick   // Print remaining characters, if there are any.
1323cab2bb3Spatrick   // Note that this will add an extra newline at the end.
1333cab2bb3Spatrick   // FIXME: buffer extra output. This would need a thread-local buffer, which
1343cab2bb3Spatrick   // on Android requires plugging into the tools (ex. ASan's) Thread class.
1353cab2bb3Spatrick   if (*p)
1363cab2bb3Spatrick     WriteOneLineToSyslog(p);
1373cab2bb3Spatrick }
1383cab2bb3Spatrick 
1393cab2bb3Spatrick static void (*sandboxing_callback)();
SetSandboxingCallback(void (* f)())1403cab2bb3Spatrick void SetSandboxingCallback(void (*f)()) {
1413cab2bb3Spatrick   sandboxing_callback = f;
1423cab2bb3Spatrick }
1433cab2bb3Spatrick 
InitAligned(uptr size,uptr align,const char * name)1441f9cb04fSpatrick uptr ReservedAddressRange::InitAligned(uptr size, uptr align,
1451f9cb04fSpatrick                                        const char *name) {
1461f9cb04fSpatrick   CHECK(IsPowerOfTwo(align));
1471f9cb04fSpatrick   if (align <= GetPageSizeCached())
1481f9cb04fSpatrick     return Init(size, name);
1491f9cb04fSpatrick   uptr start = Init(size + align, name);
1501f9cb04fSpatrick   start += align - (start & (align - 1));
1511f9cb04fSpatrick   return start;
1521f9cb04fSpatrick }
1531f9cb04fSpatrick 
154d89ec533Spatrick #if !SANITIZER_FUCHSIA
155d89ec533Spatrick 
156d89ec533Spatrick // Reserve memory range [beg, end].
157d89ec533Spatrick // We need to use inclusive range because end+1 may not be representable.
ReserveShadowMemoryRange(uptr beg,uptr end,const char * name,bool madvise_shadow)158d89ec533Spatrick void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name,
159d89ec533Spatrick                               bool madvise_shadow) {
160d89ec533Spatrick   CHECK_EQ((beg % GetMmapGranularity()), 0);
161d89ec533Spatrick   CHECK_EQ(((end + 1) % GetMmapGranularity()), 0);
162d89ec533Spatrick   uptr size = end - beg + 1;
163d89ec533Spatrick   DecreaseTotalMmap(size);  // Don't count the shadow against mmap_limit_mb.
164d89ec533Spatrick   if (madvise_shadow ? !MmapFixedSuperNoReserve(beg, size, name)
165d89ec533Spatrick                      : !MmapFixedNoReserve(beg, size, name)) {
166d89ec533Spatrick     Report(
167d89ec533Spatrick         "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
168d89ec533Spatrick         "Perhaps you're using ulimit -v\n",
169d89ec533Spatrick         size);
170d89ec533Spatrick     Abort();
171d89ec533Spatrick   }
172d89ec533Spatrick   if (madvise_shadow && common_flags()->use_madv_dontdump)
173d89ec533Spatrick     DontDumpShadowMemory(beg, size);
174d89ec533Spatrick }
175d89ec533Spatrick 
ProtectGap(uptr addr,uptr size,uptr zero_base_shadow_start,uptr zero_base_max_shadow_start)176d89ec533Spatrick void ProtectGap(uptr addr, uptr size, uptr zero_base_shadow_start,
177d89ec533Spatrick                 uptr zero_base_max_shadow_start) {
178d89ec533Spatrick   if (!size)
179d89ec533Spatrick     return;
180d89ec533Spatrick   void *res = MmapFixedNoAccess(addr, size, "shadow gap");
181d89ec533Spatrick   if (addr == (uptr)res)
182d89ec533Spatrick     return;
183d89ec533Spatrick   // A few pages at the start of the address space can not be protected.
184d89ec533Spatrick   // But we really want to protect as much as possible, to prevent this memory
185d89ec533Spatrick   // being returned as a result of a non-FIXED mmap().
186d89ec533Spatrick   if (addr == zero_base_shadow_start) {
187d89ec533Spatrick     uptr step = GetMmapGranularity();
188d89ec533Spatrick     while (size > step && addr < zero_base_max_shadow_start) {
189d89ec533Spatrick       addr += step;
190d89ec533Spatrick       size -= step;
191d89ec533Spatrick       void *res = MmapFixedNoAccess(addr, size, "shadow gap");
192d89ec533Spatrick       if (addr == (uptr)res)
193d89ec533Spatrick         return;
194d89ec533Spatrick     }
195d89ec533Spatrick   }
196d89ec533Spatrick 
197d89ec533Spatrick   Report(
198d89ec533Spatrick       "ERROR: Failed to protect the shadow gap. "
199d89ec533Spatrick       "%s cannot proceed correctly. ABORTING.\n",
200d89ec533Spatrick       SanitizerToolName);
201d89ec533Spatrick   DumpProcessMap();
202d89ec533Spatrick   Die();
203d89ec533Spatrick }
204d89ec533Spatrick 
205d89ec533Spatrick #endif  // !SANITIZER_FUCHSIA
206d89ec533Spatrick 
207*810390e3Srobert #if !SANITIZER_WINDOWS && !SANITIZER_GO
208*810390e3Srobert // Weak default implementation for when sanitizer_stackdepot is not linked in.
StackDepotStopBackgroundThread()209*810390e3Srobert SANITIZER_WEAK_ATTRIBUTE void StackDepotStopBackgroundThread() {}
StopStackDepotBackgroundThread()210*810390e3Srobert static void StopStackDepotBackgroundThread() {
211*810390e3Srobert   StackDepotStopBackgroundThread();
212*810390e3Srobert }
213*810390e3Srobert #else
214*810390e3Srobert // SANITIZER_WEAK_ATTRIBUTE is unsupported.
StopStackDepotBackgroundThread()215*810390e3Srobert static void StopStackDepotBackgroundThread() {}
216*810390e3Srobert #endif
217*810390e3Srobert 
2183cab2bb3Spatrick }  // namespace __sanitizer
2193cab2bb3Spatrick 
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_sandbox_on_notify,__sanitizer_sandbox_arguments * args)2203cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify,
2213cab2bb3Spatrick                              __sanitizer_sandbox_arguments *args) {
222*810390e3Srobert   __sanitizer::StopStackDepotBackgroundThread();
2233cab2bb3Spatrick   __sanitizer::PlatformPrepareForSandboxing(args);
2243cab2bb3Spatrick   if (__sanitizer::sandboxing_callback)
2253cab2bb3Spatrick     __sanitizer::sandboxing_callback();
2263cab2bb3Spatrick }
227