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