292 lines · cpp
1//===-- asan_linux.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-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of AddressSanitizer, an address sanity checker.10//11// Linux-specific details.12//===----------------------------------------------------------------------===//13 14#include "sanitizer_common/sanitizer_platform.h"15#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \16 SANITIZER_SOLARIS || SANITIZER_HAIKU17 18# if SANITIZER_HAIKU19# define _DEFAULT_SOURCE20# endif21 22# include <dlfcn.h>23# include <fcntl.h>24# include <limits.h>25# include <pthread.h>26# include <stdio.h>27# include <sys/mman.h>28# include <sys/resource.h>29# if !SANITIZER_HAIKU30# include <sys/syscall.h>31# endif32# include <sys/time.h>33# include <sys/types.h>34# include <unistd.h>35# include <unwind.h>36 37# include "asan_interceptors.h"38# include "asan_internal.h"39# include "asan_premap_shadow.h"40# include "asan_thread.h"41# include "sanitizer_common/sanitizer_flags.h"42# include "sanitizer_common/sanitizer_hash.h"43# include "sanitizer_common/sanitizer_libc.h"44# include "sanitizer_common/sanitizer_procmaps.h"45 46# if SANITIZER_FREEBSD || SANITIZER_HAIKU47# include <sys/link_elf.h>48# endif49 50# if SANITIZER_LINUX51# include <sys/personality.h>52# endif53 54# if SANITIZER_SOLARIS55# include <link.h>56# endif57 58# if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS59# include <ucontext.h>60# elif SANITIZER_NETBSD61# include <link_elf.h>62# include <ucontext.h>63# elif SANITIZER_HAIKU64extern "C" void *_DYNAMIC;65# else66# include <link.h>67# include <sys/ucontext.h>68# endif69 70typedef enum {71 ASAN_RT_VERSION_UNDEFINED = 0,72 ASAN_RT_VERSION_DYNAMIC,73 ASAN_RT_VERSION_STATIC,74} asan_rt_version_t;75 76// FIXME: perhaps also store abi version here?77extern "C" {78SANITIZER_INTERFACE_ATTRIBUTE79asan_rt_version_t __asan_rt_version;80}81 82namespace __asan {83 84void InitializePlatformInterceptors() {}85void InitializePlatformExceptionHandlers() {}86bool IsSystemHeapAddress(uptr addr) { return false; }87 88# if ASAN_PREMAP_SHADOW89uptr FindPremappedShadowStart(uptr shadow_size_bytes) {90 uptr granularity = GetMmapGranularity();91 uptr shadow_start = reinterpret_cast<uptr>(&__asan_shadow);92 uptr premap_shadow_size = PremapShadowSize();93 uptr shadow_size = RoundUpTo(shadow_size_bytes, granularity);94 // We may have mapped too much. Release extra memory.95 UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size);96 return shadow_start;97}98# endif99 100uptr FindDynamicShadowStart() {101 uptr shadow_size_bytes = MemToShadowSize(kHighMemEnd);102# if ASAN_PREMAP_SHADOW103 if (!PremapShadowFailed())104 return FindPremappedShadowStart(shadow_size_bytes);105# endif106 107 return MapDynamicShadow(shadow_size_bytes, ASAN_SHADOW_SCALE,108 /*min_shadow_base_alignment*/ 0, kHighMemEnd,109 GetMmapGranularity());110}111 112void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {113 UNIMPLEMENTED();114}115 116void FlushUnneededASanShadowMemory(uptr p, uptr size) {117 // Since asan's mapping is compacting, the shadow chunk may be118 // not page-aligned, so we only flush the page-aligned portion.119 ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));120}121 122void TryReExecWithoutASLR() {123# if SANITIZER_LINUX124 // ASLR personality check.125 // Caution: 'personality' is sometimes forbidden by sandboxes, so only call126 // this function as a last resort (when the memory mapping is incompatible127 // and ASan would fail anyway).128 int old_personality = personality(0xffffffff);129 if (old_personality == -1) {130 VReport(1, "WARNING: unable to run personality check.\n");131 return;132 }133 134 bool aslr_on = (old_personality & ADDR_NO_RANDOMIZE) == 0;135 136 if (aslr_on) {137 // Disable ASLR if the memory layout was incompatible.138 // Alternatively, we could just keep re-execing until we get lucky139 // with a compatible randomized layout, but the risk is that if it's140 // not an ASLR-related issue, we will be stuck in an infinite loop of141 // re-execing (unless we change ReExec to pass a parameter of the142 // number of retries allowed.)143 VReport(1,144 "WARNING: AddressSanitizer: memory layout is incompatible, "145 "possibly due to high-entropy ASLR.\n"146 "Re-execing with fixed virtual address space.\n"147 "N.B. reducing ASLR entropy is preferable.\n");148 CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);149 150 ReExec();151 }152# endif153}154 155# if SANITIZER_ANDROID156// FIXME: should we do anything for Android?157void AsanCheckDynamicRTPrereqs() {}158void AsanCheckIncompatibleRT() {}159# else160static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size,161 void *data) {162 VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n", info->dlpi_name,163 (void *)info->dlpi_addr);164 165 const char **name = (const char **)data;166 167 // Ignore first entry (the main program)168 if (!*name) {169 *name = "";170 return 0;171 }172 173# if SANITIZER_HAIKU174 if (!info->dlpi_name[0] ||175 internal_strncmp(info->dlpi_name, "/boot/system/runtime_loader",176 sizeof("/boot/system/runtime_loader") - 1) == 0)177 return 0;178# endif179# if SANITIZER_LINUX180 // Ignore vDSO. glibc versions earlier than 2.15 (and some patched181 // by distributors) return an empty name for the vDSO entry, so182 // detect this as well.183 if (!info->dlpi_name[0] ||184 internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0)185 return 0;186# endif187# if SANITIZER_FREEBSD188 // Ignore vDSO.189 if (internal_strcmp(info->dlpi_name, "[vdso]") == 0)190 return 0;191# endif192 193 *name = info->dlpi_name;194 return 1;195}196 197static bool IsDynamicRTName(const char *libname) {198 return internal_strstr(libname, "libclang_rt.asan") ||199 internal_strstr(libname, "libasan.so");200}201 202static void ReportIncompatibleRT() {203 Report("Your application is linked against incompatible ASan runtimes.\n");204 Die();205}206 207void AsanCheckDynamicRTPrereqs() {208 if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order)209 return;210 211 // Ensure that dynamic RT is the first DSO in the list212 const char *first_dso_name = nullptr;213 dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name);214 if (first_dso_name && first_dso_name[0] && !IsDynamicRTName(first_dso_name)) {215 Report(216 "ASan runtime does not come first in initial library list; "217 "you should either link runtime to your application or "218 "manually preload it with LD_PRELOAD.\n");219 Die();220 }221}222 223void AsanCheckIncompatibleRT() {224 if (ASAN_DYNAMIC) {225 if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {226 __asan_rt_version = ASAN_RT_VERSION_DYNAMIC;227 } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) {228 ReportIncompatibleRT();229 }230 } else {231 if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {232 // Ensure that dynamic runtime is not present. We should detect it233 // as early as possible, otherwise ASan interceptors could bind to234 // the functions in dynamic ASan runtime instead of the functions in235 // system libraries, causing crashes later in ASan initialization.236 MemoryMappingLayout proc_maps(/*cache_enabled*/ true);237 char filename[PATH_MAX];238 MemoryMappedSegment segment(filename, sizeof(filename));239 while (proc_maps.Next(&segment)) {240 if (IsDynamicRTName(segment.filename)) {241 ReportIncompatibleRT();242 }243 }244 __asan_rt_version = ASAN_RT_VERSION_STATIC;245 } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) {246 ReportIncompatibleRT();247 }248 }249}250# endif // SANITIZER_ANDROID251 252# if ASAN_INTERCEPT_SWAPCONTEXT253constexpr u32 kAsanContextStackFlagsMagic = 0x51260eea;254 255static int HashContextStack(const ucontext_t &ucp) {256 MurMur2Hash64Builder hash(kAsanContextStackFlagsMagic);257 hash.add(reinterpret_cast<uptr>(ucp.uc_stack.ss_sp));258 hash.add(ucp.uc_stack.ss_size);259 return static_cast<int>(hash.get());260}261 262void SignContextStack(void *context) {263 ucontext_t *ucp = reinterpret_cast<ucontext_t *>(context);264 ucp->uc_stack.ss_flags = HashContextStack(*ucp);265}266 267void ReadContextStack(void *context, uptr *stack, uptr *ssize) {268 const ucontext_t *ucp = reinterpret_cast<const ucontext_t *>(context);269 if (HashContextStack(*ucp) == ucp->uc_stack.ss_flags) {270 *stack = reinterpret_cast<uptr>(ucp->uc_stack.ss_sp);271 *ssize = ucp->uc_stack.ss_size;272 return;273 }274 *stack = 0;275 *ssize = 0;276}277# endif // ASAN_INTERCEPT_SWAPCONTEXT278 279void *AsanDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, sym); }280 281bool HandleDlopenInit() {282 // Not supported on this platform.283 static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,284 "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");285 return false;286}287 288} // namespace __asan289 290#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||291 // SANITIZER_SOLARIS || SANITIZER_HAIKU292