| 1 | //===-- sanitizer_common_libcdep.cpp --------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file is shared between AddressSanitizer and ThreadSanitizer |
| 10 | // run-time libraries. |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "sanitizer_allocator.h" |
| 14 | #include "sanitizer_allocator_interface.h" |
| 15 | #include "sanitizer_common.h" |
| 16 | #include "sanitizer_flags.h" |
| 17 | #include "sanitizer_interface_internal.h" |
| 18 | #include "sanitizer_procmaps.h" |
| 19 | #include "sanitizer_stackdepot.h" |
| 20 | |
| 21 | namespace __sanitizer { |
| 22 | |
| 23 | #if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO |
| 24 | // Weak default implementation for when sanitizer_stackdepot is not linked in. |
| 25 | SANITIZER_WEAK_ATTRIBUTE StackDepotStats StackDepotGetStats() { return {}; } |
| 26 | |
| 27 | void *BackgroundThread(void *arg) { |
| 28 | VPrintf(1, "%s: Started BackgroundThread\n" , SanitizerToolName); |
| 29 | const uptr = common_flags()->hard_rss_limit_mb; |
| 30 | const uptr = common_flags()->soft_rss_limit_mb; |
| 31 | const bool heap_profile = common_flags()->heap_profile; |
| 32 | uptr = 0; |
| 33 | uptr prev_reported_stack_depot_size = 0; |
| 34 | bool = false; |
| 35 | uptr = 0; |
| 36 | while (true) { |
| 37 | SleepForMillis(millis: 100); |
| 38 | const uptr = GetRSS() >> 20; |
| 39 | if (Verbosity()) { |
| 40 | // If RSS has grown 10% since last time, print some information. |
| 41 | if (prev_reported_rss * 11 / 10 < current_rss_mb) { |
| 42 | Printf(format: "%s: RSS: %zdMb\n" , SanitizerToolName, current_rss_mb); |
| 43 | prev_reported_rss = current_rss_mb; |
| 44 | } |
| 45 | // If stack depot has grown 10% since last time, print it too. |
| 46 | StackDepotStats stack_depot_stats = StackDepotGetStats(); |
| 47 | if (prev_reported_stack_depot_size * 11 / 10 < |
| 48 | stack_depot_stats.allocated) { |
| 49 | Printf(format: "%s: StackDepot: %zd ids; %zdM allocated\n" , SanitizerToolName, |
| 50 | stack_depot_stats.n_uniq_ids, stack_depot_stats.allocated >> 20); |
| 51 | prev_reported_stack_depot_size = stack_depot_stats.allocated; |
| 52 | } |
| 53 | } |
| 54 | // Check RSS against the limit. |
| 55 | if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) { |
| 56 | Report(format: "%s: hard rss limit exhausted (%zdMb vs %zdMb)\n" , |
| 57 | SanitizerToolName, hard_rss_limit_mb, current_rss_mb); |
| 58 | DumpProcessMap(); |
| 59 | Die(); |
| 60 | } |
| 61 | if (soft_rss_limit_mb) { |
| 62 | if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) { |
| 63 | reached_soft_rss_limit = true; |
| 64 | Report(format: "%s: soft rss limit exhausted (%zdMb vs %zdMb)\n" , |
| 65 | SanitizerToolName, soft_rss_limit_mb, current_rss_mb); |
| 66 | SetRssLimitExceeded(true); |
| 67 | } else if (soft_rss_limit_mb >= current_rss_mb && |
| 68 | reached_soft_rss_limit) { |
| 69 | reached_soft_rss_limit = false; |
| 70 | Report(format: "%s: soft rss limit unexhausted (%zdMb vs %zdMb)\n" , |
| 71 | SanitizerToolName, soft_rss_limit_mb, current_rss_mb); |
| 72 | SetRssLimitExceeded(false); |
| 73 | } |
| 74 | } |
| 75 | if (heap_profile && |
| 76 | current_rss_mb > rss_during_last_reported_profile * 1.1) { |
| 77 | Printf(format: "\n\nHEAP PROFILE at RSS %zdMb\n" , current_rss_mb); |
| 78 | __sanitizer_print_memory_profile(top_percent: 90, max_number_of_contexts: 20); |
| 79 | rss_during_last_reported_profile = current_rss_mb; |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | void MaybeStartBackgroudThread() { |
| 85 | // Need to implement/test on other platforms. |
| 86 | // Start the background thread if one of the rss limits is given. |
| 87 | if (!common_flags()->hard_rss_limit_mb && |
| 88 | !common_flags()->soft_rss_limit_mb && |
| 89 | !common_flags()->heap_profile) return; |
| 90 | if (!&internal_pthread_create) { |
| 91 | VPrintf(1, "%s: internal_pthread_create undefined\n" , SanitizerToolName); |
| 92 | return; // Can't spawn the thread anyway. |
| 93 | } |
| 94 | |
| 95 | static bool started = false; |
| 96 | if (!started) { |
| 97 | started = true; |
| 98 | internal_start_thread(func: BackgroundThread, arg: nullptr); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | # if !SANITIZER_START_BACKGROUND_THREAD_IN_ASAN_INTERNAL |
| 103 | # ifdef __clang__ |
| 104 | # pragma clang diagnostic push |
| 105 | // We avoid global-constructors to be sure that globals are ready when |
| 106 | // sanitizers need them. This can happend before global constructors executed. |
| 107 | // Here we don't mind if thread is started on later stages. |
| 108 | # pragma clang diagnostic ignored "-Wglobal-constructors" |
| 109 | # endif |
| 110 | static struct BackgroudThreadStarted { |
| 111 | BackgroudThreadStarted() { MaybeStartBackgroudThread(); } |
| 112 | } background_thread_strarter UNUSED; |
| 113 | # ifdef __clang__ |
| 114 | # pragma clang diagnostic pop |
| 115 | # endif |
| 116 | # endif |
| 117 | #else |
| 118 | void MaybeStartBackgroudThread() {} |
| 119 | #endif |
| 120 | |
| 121 | void WriteToSyslog(const char *msg) { |
| 122 | if (!msg) |
| 123 | return; |
| 124 | InternalScopedString msg_copy; |
| 125 | msg_copy.Append(str: msg); |
| 126 | const char *p = msg_copy.data(); |
| 127 | |
| 128 | // Print one line at a time. |
| 129 | // syslog, at least on Android, has an implicit message length limit. |
| 130 | while (char* q = internal_strchr(s: p, c: '\n')) { |
| 131 | *q = '\0'; |
| 132 | WriteOneLineToSyslog(s: p); |
| 133 | p = q + 1; |
| 134 | } |
| 135 | // Print remaining characters, if there are any. |
| 136 | // Note that this will add an extra newline at the end. |
| 137 | // FIXME: buffer extra output. This would need a thread-local buffer, which |
| 138 | // on Android requires plugging into the tools (ex. ASan's) Thread class. |
| 139 | if (*p) |
| 140 | WriteOneLineToSyslog(s: p); |
| 141 | } |
| 142 | |
| 143 | static void (*sandboxing_callback)(); |
| 144 | void SetSandboxingCallback(void (*f)()) { |
| 145 | sandboxing_callback = f; |
| 146 | } |
| 147 | |
| 148 | uptr ReservedAddressRange::InitAligned(uptr size, uptr align, |
| 149 | const char *name) { |
| 150 | CHECK(IsPowerOfTwo(align)); |
| 151 | if (align <= GetPageSizeCached()) |
| 152 | return Init(size, name); |
| 153 | uptr start = Init(size: size + align, name); |
| 154 | start += align - (start & (align - 1)); |
| 155 | return start; |
| 156 | } |
| 157 | |
| 158 | #if !SANITIZER_FUCHSIA |
| 159 | |
| 160 | // Reserve memory range [beg, end]. |
| 161 | // We need to use inclusive range because end+1 may not be representable. |
| 162 | void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name, |
| 163 | bool madvise_shadow) { |
| 164 | CHECK_EQ((beg % GetMmapGranularity()), 0); |
| 165 | CHECK_EQ(((end + 1) % GetMmapGranularity()), 0); |
| 166 | uptr size = end - beg + 1; |
| 167 | DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. |
| 168 | if (madvise_shadow ? !MmapFixedSuperNoReserve(fixed_addr: beg, size, name) |
| 169 | : !MmapFixedNoReserve(fixed_addr: beg, size, name)) { |
| 170 | Report( |
| 171 | format: "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " |
| 172 | "Perhaps you're using ulimit -v or ulimit -d\n" , |
| 173 | size); |
| 174 | Die(); |
| 175 | } |
| 176 | if (madvise_shadow && common_flags()->use_madv_dontdump) |
| 177 | DontDumpShadowMemory(addr: beg, length: size); |
| 178 | } |
| 179 | |
| 180 | void ProtectGap(uptr addr, uptr size, uptr zero_base_shadow_start, |
| 181 | uptr zero_base_max_shadow_start) { |
| 182 | if (!size) |
| 183 | return; |
| 184 | void *res = MmapFixedNoAccess(fixed_addr: addr, size, name: "shadow gap" ); |
| 185 | if (addr == (uptr)res) |
| 186 | return; |
| 187 | // A few pages at the start of the address space can not be protected. |
| 188 | // But we really want to protect as much as possible, to prevent this memory |
| 189 | // being returned as a result of a non-FIXED mmap(). |
| 190 | if (addr == zero_base_shadow_start) { |
| 191 | uptr step = GetMmapGranularity(); |
| 192 | while (size > step && addr < zero_base_max_shadow_start) { |
| 193 | addr += step; |
| 194 | size -= step; |
| 195 | void *res = MmapFixedNoAccess(fixed_addr: addr, size, name: "shadow gap" ); |
| 196 | if (addr == (uptr)res) |
| 197 | return; |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | Report( |
| 202 | format: "ERROR: Failed to protect the shadow gap. " |
| 203 | "%s cannot proceed correctly. ABORTING.\n" , |
| 204 | SanitizerToolName); |
| 205 | DumpProcessMap(); |
| 206 | Die(); |
| 207 | } |
| 208 | |
| 209 | #endif // !SANITIZER_FUCHSIA |
| 210 | |
| 211 | #if !SANITIZER_WINDOWS && !SANITIZER_GO |
| 212 | // Weak default implementation for when sanitizer_stackdepot is not linked in. |
| 213 | SANITIZER_WEAK_ATTRIBUTE void StackDepotStopBackgroundThread() {} |
| 214 | static void StopStackDepotBackgroundThread() { |
| 215 | StackDepotStopBackgroundThread(); |
| 216 | } |
| 217 | #else |
| 218 | // SANITIZER_WEAK_ATTRIBUTE is unsupported. |
| 219 | static void StopStackDepotBackgroundThread() {} |
| 220 | #endif |
| 221 | |
| 222 | void MemCpyAccessible(void *dest, const void *src, uptr n) { |
| 223 | if (TryMemCpy(dest, src, n)) |
| 224 | return; |
| 225 | |
| 226 | const uptr page_size = GetPageSize(); |
| 227 | uptr b = reinterpret_cast<uptr>(src); |
| 228 | uptr b_up = RoundUpTo(size: b, boundary: page_size); |
| 229 | |
| 230 | uptr e = reinterpret_cast<uptr>(src) + n; |
| 231 | uptr e_down = RoundDownTo(x: e, boundary: page_size); |
| 232 | |
| 233 | auto copy_or_zero = [dest, src](uptr beg, uptr end) { |
| 234 | const uptr udest = reinterpret_cast<uptr>(dest); |
| 235 | const uptr usrc = reinterpret_cast<uptr>(src); |
| 236 | void *d = reinterpret_cast<void *>(udest + (beg - usrc)); |
| 237 | const uptr size = end - beg; |
| 238 | if (!TryMemCpy(dest: d, src: reinterpret_cast<void *>(beg), n: size)) |
| 239 | internal_memset(s: d, c: 0, n: size); |
| 240 | }; |
| 241 | |
| 242 | copy_or_zero(b, b_up); |
| 243 | for (uptr p = b_up; p < e_down; p += page_size) |
| 244 | copy_or_zero(p, p + page_size); |
| 245 | copy_or_zero(e_down, e); |
| 246 | } |
| 247 | |
| 248 | } // namespace __sanitizer |
| 249 | |
| 250 | SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify, |
| 251 | __sanitizer_sandbox_arguments *args) { |
| 252 | __sanitizer::StopStackDepotBackgroundThread(); |
| 253 | __sanitizer::PlatformPrepareForSandboxing(args); |
| 254 | if (__sanitizer::sandboxing_callback) |
| 255 | __sanitizer::sandboxing_callback(); |
| 256 | } |
| 257 | |