168d75effSDimitry Andric //===-- sanitizer_common_libcdep.cpp --------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is shared between AddressSanitizer and ThreadSanitizer
1068d75effSDimitry Andric // run-time libraries.
1168d75effSDimitry Andric //===----------------------------------------------------------------------===//
1268d75effSDimitry Andric 
130eae32dcSDimitry Andric #include "sanitizer_allocator.h"
1468d75effSDimitry Andric #include "sanitizer_allocator_interface.h"
1568d75effSDimitry Andric #include "sanitizer_common.h"
1668d75effSDimitry Andric #include "sanitizer_flags.h"
1781ad6265SDimitry Andric #include "sanitizer_interface_internal.h"
1868d75effSDimitry Andric #include "sanitizer_procmaps.h"
190eae32dcSDimitry Andric #include "sanitizer_stackdepot.h"
2068d75effSDimitry Andric 
2168d75effSDimitry Andric namespace __sanitizer {
2268d75effSDimitry Andric 
2368d75effSDimitry Andric #if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO
2468d75effSDimitry Andric // Weak default implementation for when sanitizer_stackdepot is not linked in.
StackDepotGetStats()25349cc55cSDimitry Andric SANITIZER_WEAK_ATTRIBUTE StackDepotStats StackDepotGetStats() { return {}; }
2668d75effSDimitry Andric 
BackgroundThread(void * arg)275ffd83dbSDimitry Andric void *BackgroundThread(void *arg) {
280eae32dcSDimitry Andric   VPrintf(1, "%s: Started BackgroundThread\n", SanitizerToolName);
2968d75effSDimitry Andric   const uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
3068d75effSDimitry Andric   const uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
3168d75effSDimitry Andric   const bool heap_profile = common_flags()->heap_profile;
3268d75effSDimitry Andric   uptr prev_reported_rss = 0;
3368d75effSDimitry Andric   uptr prev_reported_stack_depot_size = 0;
3468d75effSDimitry Andric   bool reached_soft_rss_limit = false;
3568d75effSDimitry Andric   uptr rss_during_last_reported_profile = 0;
3668d75effSDimitry Andric   while (true) {
3768d75effSDimitry Andric     SleepForMillis(100);
3868d75effSDimitry Andric     const uptr current_rss_mb = GetRSS() >> 20;
3968d75effSDimitry Andric     if (Verbosity()) {
4068d75effSDimitry Andric       // If RSS has grown 10% since last time, print some information.
4168d75effSDimitry Andric       if (prev_reported_rss * 11 / 10 < current_rss_mb) {
4268d75effSDimitry Andric         Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
4368d75effSDimitry Andric         prev_reported_rss = current_rss_mb;
4468d75effSDimitry Andric       }
4568d75effSDimitry Andric       // If stack depot has grown 10% since last time, print it too.
46349cc55cSDimitry Andric       StackDepotStats stack_depot_stats = StackDepotGetStats();
4768d75effSDimitry Andric       if (prev_reported_stack_depot_size * 11 / 10 <
48349cc55cSDimitry Andric           stack_depot_stats.allocated) {
49349cc55cSDimitry Andric         Printf("%s: StackDepot: %zd ids; %zdM allocated\n", SanitizerToolName,
50349cc55cSDimitry Andric                stack_depot_stats.n_uniq_ids, stack_depot_stats.allocated >> 20);
51349cc55cSDimitry Andric         prev_reported_stack_depot_size = stack_depot_stats.allocated;
5268d75effSDimitry Andric       }
5368d75effSDimitry Andric     }
5468d75effSDimitry Andric     // Check RSS against the limit.
5568d75effSDimitry Andric     if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
5668d75effSDimitry Andric       Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
5768d75effSDimitry Andric              SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
5868d75effSDimitry Andric       DumpProcessMap();
5968d75effSDimitry Andric       Die();
6068d75effSDimitry Andric     }
6168d75effSDimitry Andric     if (soft_rss_limit_mb) {
6268d75effSDimitry Andric       if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
6368d75effSDimitry Andric         reached_soft_rss_limit = true;
6468d75effSDimitry Andric         Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
6568d75effSDimitry Andric                SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
660eae32dcSDimitry Andric         SetRssLimitExceeded(true);
6768d75effSDimitry Andric       } else if (soft_rss_limit_mb >= current_rss_mb &&
6868d75effSDimitry Andric                  reached_soft_rss_limit) {
6968d75effSDimitry Andric         reached_soft_rss_limit = false;
7006c3fb27SDimitry Andric         Report("%s: soft rss limit unexhausted (%zdMb vs %zdMb)\n",
7106c3fb27SDimitry Andric                SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
720eae32dcSDimitry Andric         SetRssLimitExceeded(false);
7368d75effSDimitry Andric       }
7468d75effSDimitry Andric     }
7568d75effSDimitry Andric     if (heap_profile &&
7668d75effSDimitry Andric         current_rss_mb > rss_during_last_reported_profile * 1.1) {
7768d75effSDimitry Andric       Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb);
7868d75effSDimitry Andric       __sanitizer_print_memory_profile(90, 20);
7968d75effSDimitry Andric       rss_during_last_reported_profile = current_rss_mb;
8068d75effSDimitry Andric     }
8168d75effSDimitry Andric   }
8268d75effSDimitry Andric }
830eae32dcSDimitry Andric 
MaybeStartBackgroudThread()840eae32dcSDimitry Andric void MaybeStartBackgroudThread() {
850eae32dcSDimitry Andric   // Need to implement/test on other platforms.
860eae32dcSDimitry Andric   // Start the background thread if one of the rss limits is given.
870eae32dcSDimitry Andric   if (!common_flags()->hard_rss_limit_mb &&
880eae32dcSDimitry Andric       !common_flags()->soft_rss_limit_mb &&
890eae32dcSDimitry Andric       !common_flags()->heap_profile) return;
900eae32dcSDimitry Andric   if (!&real_pthread_create) {
910eae32dcSDimitry Andric     VPrintf(1, "%s: real_pthread_create undefined\n", SanitizerToolName);
920eae32dcSDimitry Andric     return;  // Can't spawn the thread anyway.
930eae32dcSDimitry Andric   }
940eae32dcSDimitry Andric 
950eae32dcSDimitry Andric   static bool started = false;
960eae32dcSDimitry Andric   if (!started) {
970eae32dcSDimitry Andric     started = true;
980eae32dcSDimitry Andric     internal_start_thread(BackgroundThread, nullptr);
990eae32dcSDimitry Andric   }
1000eae32dcSDimitry Andric }
1010eae32dcSDimitry Andric 
1020eae32dcSDimitry Andric #  if !SANITIZER_START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
10381ad6265SDimitry Andric #    ifdef __clang__
1040eae32dcSDimitry Andric #    pragma clang diagnostic push
1050eae32dcSDimitry Andric // We avoid global-constructors to be sure that globals are ready when
1060eae32dcSDimitry Andric // sanitizers need them. This can happend before global constructors executed.
1070eae32dcSDimitry Andric // Here we don't mind if thread is started on later stages.
1080eae32dcSDimitry Andric #    pragma clang diagnostic ignored "-Wglobal-constructors"
10981ad6265SDimitry Andric #    endif
1100eae32dcSDimitry Andric static struct BackgroudThreadStarted {
BackgroudThreadStarted__sanitizer::BackgroudThreadStarted1110eae32dcSDimitry Andric   BackgroudThreadStarted() { MaybeStartBackgroudThread(); }
1120eae32dcSDimitry Andric } background_thread_strarter UNUSED;
11381ad6265SDimitry Andric #    ifdef __clang__
1140eae32dcSDimitry Andric #    pragma clang diagnostic pop
1150eae32dcSDimitry Andric #    endif
11681ad6265SDimitry Andric #  endif
1170eae32dcSDimitry Andric #else
1180eae32dcSDimitry Andric void MaybeStartBackgroudThread() {}
11968d75effSDimitry Andric #endif
12068d75effSDimitry Andric 
WriteToSyslog(const char * msg)12168d75effSDimitry Andric void WriteToSyslog(const char *msg) {
122*5f757f3fSDimitry Andric   if (!msg)
123*5f757f3fSDimitry Andric     return;
124fe6060f1SDimitry Andric   InternalScopedString msg_copy;
125*5f757f3fSDimitry Andric   msg_copy.Append(msg);
126fe6060f1SDimitry Andric   const char *p = msg_copy.data();
12768d75effSDimitry Andric 
12868d75effSDimitry Andric   // Print one line at a time.
12968d75effSDimitry Andric   // syslog, at least on Android, has an implicit message length limit.
130fe6060f1SDimitry Andric   while (char* q = internal_strchr(p, '\n')) {
13168d75effSDimitry Andric     *q = '\0';
13268d75effSDimitry Andric     WriteOneLineToSyslog(p);
13368d75effSDimitry Andric     p = q + 1;
13468d75effSDimitry Andric   }
13568d75effSDimitry Andric   // Print remaining characters, if there are any.
13668d75effSDimitry Andric   // Note that this will add an extra newline at the end.
13768d75effSDimitry Andric   // FIXME: buffer extra output. This would need a thread-local buffer, which
13868d75effSDimitry Andric   // on Android requires plugging into the tools (ex. ASan's) Thread class.
13968d75effSDimitry Andric   if (*p)
14068d75effSDimitry Andric     WriteOneLineToSyslog(p);
14168d75effSDimitry Andric }
14268d75effSDimitry Andric 
14368d75effSDimitry Andric static void (*sandboxing_callback)();
SetSandboxingCallback(void (* f)())14468d75effSDimitry Andric void SetSandboxingCallback(void (*f)()) {
14568d75effSDimitry Andric   sandboxing_callback = f;
14668d75effSDimitry Andric }
14768d75effSDimitry Andric 
InitAligned(uptr size,uptr align,const char * name)1485ffd83dbSDimitry Andric uptr ReservedAddressRange::InitAligned(uptr size, uptr align,
1495ffd83dbSDimitry Andric                                        const char *name) {
1505ffd83dbSDimitry Andric   CHECK(IsPowerOfTwo(align));
1515ffd83dbSDimitry Andric   if (align <= GetPageSizeCached())
1525ffd83dbSDimitry Andric     return Init(size, name);
1535ffd83dbSDimitry Andric   uptr start = Init(size + align, name);
1545ffd83dbSDimitry Andric   start += align - (start & (align - 1));
1555ffd83dbSDimitry Andric   return start;
1565ffd83dbSDimitry Andric }
1575ffd83dbSDimitry Andric 
158fe6060f1SDimitry Andric #if !SANITIZER_FUCHSIA
159e8d8bef9SDimitry Andric 
160e8d8bef9SDimitry Andric // Reserve memory range [beg, end].
161e8d8bef9SDimitry Andric // We need to use inclusive range because end+1 may not be representable.
ReserveShadowMemoryRange(uptr beg,uptr end,const char * name,bool madvise_shadow)162e8d8bef9SDimitry Andric void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name,
163e8d8bef9SDimitry Andric                               bool madvise_shadow) {
164e8d8bef9SDimitry Andric   CHECK_EQ((beg % GetMmapGranularity()), 0);
165e8d8bef9SDimitry Andric   CHECK_EQ(((end + 1) % GetMmapGranularity()), 0);
166e8d8bef9SDimitry Andric   uptr size = end - beg + 1;
167e8d8bef9SDimitry Andric   DecreaseTotalMmap(size);  // Don't count the shadow against mmap_limit_mb.
168e8d8bef9SDimitry Andric   if (madvise_shadow ? !MmapFixedSuperNoReserve(beg, size, name)
169e8d8bef9SDimitry Andric                      : !MmapFixedNoReserve(beg, size, name)) {
170e8d8bef9SDimitry Andric     Report(
171e8d8bef9SDimitry Andric         "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
172e8d8bef9SDimitry Andric         "Perhaps you're using ulimit -v\n",
173e8d8bef9SDimitry Andric         size);
174e8d8bef9SDimitry Andric     Abort();
175e8d8bef9SDimitry Andric   }
176e8d8bef9SDimitry Andric   if (madvise_shadow && common_flags()->use_madv_dontdump)
177e8d8bef9SDimitry Andric     DontDumpShadowMemory(beg, size);
178e8d8bef9SDimitry Andric }
179e8d8bef9SDimitry Andric 
ProtectGap(uptr addr,uptr size,uptr zero_base_shadow_start,uptr zero_base_max_shadow_start)180e8d8bef9SDimitry Andric void ProtectGap(uptr addr, uptr size, uptr zero_base_shadow_start,
181e8d8bef9SDimitry Andric                 uptr zero_base_max_shadow_start) {
182e8d8bef9SDimitry Andric   if (!size)
183e8d8bef9SDimitry Andric     return;
184e8d8bef9SDimitry Andric   void *res = MmapFixedNoAccess(addr, size, "shadow gap");
185e8d8bef9SDimitry Andric   if (addr == (uptr)res)
186e8d8bef9SDimitry Andric     return;
187e8d8bef9SDimitry Andric   // A few pages at the start of the address space can not be protected.
188e8d8bef9SDimitry Andric   // But we really want to protect as much as possible, to prevent this memory
189e8d8bef9SDimitry Andric   // being returned as a result of a non-FIXED mmap().
190e8d8bef9SDimitry Andric   if (addr == zero_base_shadow_start) {
191e8d8bef9SDimitry Andric     uptr step = GetMmapGranularity();
192e8d8bef9SDimitry Andric     while (size > step && addr < zero_base_max_shadow_start) {
193e8d8bef9SDimitry Andric       addr += step;
194e8d8bef9SDimitry Andric       size -= step;
195e8d8bef9SDimitry Andric       void *res = MmapFixedNoAccess(addr, size, "shadow gap");
196e8d8bef9SDimitry Andric       if (addr == (uptr)res)
197e8d8bef9SDimitry Andric         return;
198e8d8bef9SDimitry Andric     }
199e8d8bef9SDimitry Andric   }
200e8d8bef9SDimitry Andric 
201e8d8bef9SDimitry Andric   Report(
202e8d8bef9SDimitry Andric       "ERROR: Failed to protect the shadow gap. "
203e8d8bef9SDimitry Andric       "%s cannot proceed correctly. ABORTING.\n",
204e8d8bef9SDimitry Andric       SanitizerToolName);
205e8d8bef9SDimitry Andric   DumpProcessMap();
206e8d8bef9SDimitry Andric   Die();
207e8d8bef9SDimitry Andric }
208e8d8bef9SDimitry Andric 
209fe6060f1SDimitry Andric #endif  // !SANITIZER_FUCHSIA
210e8d8bef9SDimitry Andric 
2110eae32dcSDimitry Andric #if !SANITIZER_WINDOWS && !SANITIZER_GO
2120eae32dcSDimitry Andric // Weak default implementation for when sanitizer_stackdepot is not linked in.
StackDepotStopBackgroundThread()2130eae32dcSDimitry Andric SANITIZER_WEAK_ATTRIBUTE void StackDepotStopBackgroundThread() {}
StopStackDepotBackgroundThread()2140eae32dcSDimitry Andric static void StopStackDepotBackgroundThread() {
2150eae32dcSDimitry Andric   StackDepotStopBackgroundThread();
2160eae32dcSDimitry Andric }
2170eae32dcSDimitry Andric #else
2180eae32dcSDimitry Andric // SANITIZER_WEAK_ATTRIBUTE is unsupported.
StopStackDepotBackgroundThread()2190eae32dcSDimitry Andric static void StopStackDepotBackgroundThread() {}
2200eae32dcSDimitry Andric #endif
2210eae32dcSDimitry Andric 
22268d75effSDimitry Andric }  // namespace __sanitizer
22368d75effSDimitry Andric 
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_sandbox_on_notify,__sanitizer_sandbox_arguments * args)22468d75effSDimitry Andric SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify,
22568d75effSDimitry Andric                              __sanitizer_sandbox_arguments *args) {
2260eae32dcSDimitry Andric   __sanitizer::StopStackDepotBackgroundThread();
22768d75effSDimitry Andric   __sanitizer::PlatformPrepareForSandboxing(args);
22868d75effSDimitry Andric   if (__sanitizer::sandboxing_callback)
22968d75effSDimitry Andric     __sanitizer::sandboxing_callback();
23068d75effSDimitry Andric }
231