失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > ASAN Runtime【源码分析】(一)——初始化

ASAN Runtime【源码分析】(一)——初始化

时间:2024-01-12 18:07:16

相关推荐

ASAN Runtime【源码分析】(一)——初始化

(LastCommitID=fed41342a82f5a3a919a82bf7a48313e296b)

ASAN RT初始化前的Routine_start→\rightarrow→__libc_start_main→\rightarrow→__libc_csu_init→\rightarrow→asan.module_ctor(ctor节在main函数前调用,用来执行构造函数、完成变量初始化等逻辑)→\rightarrow→__asan_init

0 关于ASAN初始化

AsanInitInternal可在main函数之前__asan_init中调用,也可在运行过程中由AsanInitFromRtl动态初始化。

AsanActivate若检查发现ASAN Deactive,就激活ASAN。(初始默认无需激活)

// compiler-rt/lib/asan/asan_rtl.cppstatic void AsanInitInternal() {...}...// Initialize as requested from some part of ASan runtime library (interceptors,// allocator, etc).void AsanInitFromRtl() {AsanInitInternal();}...// Initialize as requested from instrumented application code.// We use this call as a trigger to wake up ASan from deactivated state.void __asan_init() {AsanActivate();AsanInitInternal();}

AsanInitInternal的逻辑包含如下:

1CacheBinaryName

ReadBinaryName通过readlink系统调用从/proc/self/exe读取二进制文件镜像名存储在binary_name_cache_str,如/home/leone/文档/test/a.out

ReadProcessName/proc/self/cmdline文件中存储的进程名读取到process_name_cache_str,如a.out

// compiler-rt/lib/sanitizer_common/sanitizer_common.cpp// Call once to make sure that binary_name_cache_str is initializedvoid CacheBinaryName() {...ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));}

2InitializeFlags

根据sanitizer_flags.incasan_flags.inclsan_flags.incubsan_flags.inc初始化并(使用各自的Parser)注册各自的Flag结构体(内存分配操作用到了LowLevelAllocator::Allocate)。

Parser再进一步解析额外的环境变量选项。

确保RedzoneSize≥16RedzoneSize\geq16RedzoneSize≥16且RedzoneSize=2nRedzoneSize=2^nRedzoneSize=2n。

配置Quarantine大小(Quarantine过小会导致漏报)。配置线程局部Quarantine大小(不建议小于64KB)。

// compiler-rt/lib/asan/asan_flags.cpp// Initialize flags. This must be done early, because most of the// initialization steps look at flags().void InitializeFlags() {// Set the default values and prepare for parsing ASan and common flags.SetCommonFlagsDefaults();...Flags *f = flags();f->SetDefaults();FlagParser asan_parser;RegisterAsanFlags(&asan_parser, f);RegisterCommonFlags(&asan_parser);// Set the default values and prepare for parsing LSan and UBSan flags// (which can also overwrite common flags)....__lsan::Flags *lf = __lsan::flags();lf->SetDefaults();...__ubsan::Flags *uf = __ubsan::flags();uf->SetDefaults();...// Ensure that redzone is at least SHADOW_GRANULARITY.if (f->redzone < (int)SHADOW_GRANULARITY)f->redzone = SHADOW_GRANULARITY;...f->quarantine_size_mb = kDefaultQuarantineSizeMb;...if (f->thread_local_quarantine_size_kb < 0) {const u32 kDefaultThreadLocalQuarantineSizeKb =// It is not advised to go lower than 64Kb, otherwise quarantine batches// pushed from thread local quarantine to global one will create too// much overhead. One quarantine batch size is 8Kb and it holds up to// 1021 chunk, which amounts to 1/8 memory overhead per batch when// thread local quarantine is set to 64Kb.(ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10);f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb;}...}

3AsanCheckIncompatibleRT

记录当前进程内存映射布局(查看proc/self/maps),默认缓存起来。

检查确保内存映射中不存在名字包含libclang_rt.asanlibasan.so的Segment。最后标记为ASAN_RT_VERSION_STATIC,表示静态链接了ASAN Runtime,如libclang_rt.asan-x86_64.a

// compiler-rt/lib/asan/asan_linux.cppvoid AsanCheckIncompatibleRT() {...if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {// Ensure that dynamic runtime is not present. We should detect it// as early as possible, otherwise ASan interceptors could bind to// the functions in dynamic ASan runtime instead of the functions in// system libraries, causing crashes later in ASan initialization.MemoryMappingLayout proc_maps(/*cache_enabled*/true);char filename[PATH_MAX];MemoryMappedSegment segment(filename, sizeof(filename));while (proc_maps.Next(&segment)) {if (IsDynamicRTName(segment.filename)) {Report("Your application is linked against ""incompatible ASan runtimes.\n");Die();}}__asan_rt_version = ASAN_RT_VERSION_STATIC;} else ...}

4AsanCheckDynamicRTPrereqs

由于链接的ASAN Runtime是Static的,因此该函数不执行任何东西。

5SetCanPoisonMemory

根据poison_heap原子设置__asan::can_poison_memory,表明可以在内存(解)分配时污染堆对应的影子内存。

ASAN_FLAG(bool, poison_heap, true,"Poison (or not) the heap memory on [de]allocation. Zero value is useful ""for benchmarking the allocator or instrumentator.")

6SetMallocContextSize

根据malloc_context_size(已经从1改成了30)原子设置__asan::malloc_context_size,表明内存(解)分配出现内存错误时显示的调用栈层数。

COMMON_FLAG(int, malloc_context_size, 1,"Max number of stack frames kept for each allocation/deallocation.")

7InitializeHighMemEnd

HighMem=0x10007fff8000∼0x7fffffffffffHighMem=0x10007fff8000\sim0x7fffffffffffHighMem=0x10007fff8000∼0x7fffffffffff,对应(Shadow = (Mem >> 3) + 0x7fff8000)的ShdowMemory=0x0fff7000∼0x10007fff7fffShdowMemory=0x0fff7000\sim0x10007fff7fffShdowMemory=0x0fff7000∼0x10007fff7fff(与这一致):

kHighMemEnd = (1ULL << 47) - 1,即0x7fffffffffffkHighMemBeg = (kHighMemEnd >> 3) + (0x7FFFFFFF & (~0xFFFULL << 3)) + 1,即0x10007fff8000。(其中3是kDefaultShadowScale

8AsanDoesNotSupportStaticLinkage

Make sure we are not statically linked.

确保存在Dynamic Seciton_Dynamic。(?)

9 一些简单配置

10InitializeAsanInterceptors

10.1InitializeCommonInterceptors

Placement New一个interceptor_metadata_map

INIT_MMAP开始逐一初始化通用函数拦截器。在此之前,会有一系列变量、函数指针(mmap_type__interception::real_mmapmmap__interceptor_mmap)被初始化了,如下所示(每个函数的拦截的具体过程会有区别):

// compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc#if SANITIZER_INTERCEPT_MMAP// INTERCEPTOR Macro (compiler-rt/lib/interception/interception.h) Example:// typedef void *(*mmap_type)(void *, SIZE_T, int, int, int, OFF_T);// namespace __interception { mmap_type real_mmap; }// extern "C" void * mmap(void *, SIZE_T, int, int, int, OFF_T) __attribute__((weak, alias("__interceptor_mmap"), visibility("default")));// extern "C" __attribute__((visibility("default"))) void * __interceptor_mmap(void *, SIZE_T, int, int, int, OFF_T)INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags, int fd, OFF_T off) {void *ctx;if (common_flags()->detect_write_exec)ReportMmapWriteExec(prot);if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)return (void *)internal_mmap(addr, sz, prot, flags, fd, off); // 如果ASAN尚未初始化完成,__interceptor_mmap依然使用internal_mmap// COMMON_INTERCEPTOR_ENTER Macro (compiler-rt/lib/asan/asan_interceptors.cpp) Example:// AsanInterceptorContext _ctx = {mmap};// ctx = (void *)&_ctx;// (void) ctx; // 当前例子尚未用到// if (asan_init_is_running)// return __interception::real_mmap(__VA_ARGS__); //初始化还在进行中,先使用真的// CHECK(!asan_init_is_running);// if (UNLIKELY(!asan_inited)) {// AsanInitFromRtl();COMMON_INTERCEPTOR_ENTER(ctx, mmap, addr, sz, prot, flags, fd, off);// Example: { return __interception::real_mmap(addr, sz, prot, flags, fd, off); } // 初始化完成,已明确__interception::real_mmap并使用COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, sz, prot, flags, fd, off);}#endif

INIT_MMAP会调用InterceptFunction,其中通过dlsym找到/usr/lib/x86_64-linux-gnu/libc-2.31.so中的mmap64,赋值给__interception::real_mmap

// compiler-rt/lib/interception/interception_linux.cpp// InterceptFunction("mmap", 0x53ef00 <__interception::real_mmap>, mmap(weak alias to)->0x436ae0 <__interceptor_mmap(...)>, 0x436ae0 <__interceptor_mmap(...)>)bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func,uptr wrapper) {void *addr = GetFuncAddr(name, wrapper);*ptr_to_real = (uptr)addr;return addr && (func == wrapper);}

(请注意__interceptor_mmap是函数名,__interception::real_mmap是函数指针,关系粗略可以理解为:函数指针 = &函数名

*__interceptor_mmap{void *(void *, SIZE_T, int, int, int, OFF_T)} 0x436ae0 <__interceptor_mmap(void*, SIZE_T, int, int, int, OFF_T)>Attempt to take contents of a non-pointer value.__interceptor_mmap{void *(void *, SIZE_T, int, int, int, OFF_T)} 0x436ae0 <__interceptor_mmap(void*, SIZE_T, int, int, int, OFF_T)>type = void *(void *, SIZE_T, int, int, int, OFF_T)&__interceptor_mmap0x436ae0 <__interceptor_mmap(void*, SIZE_T, int, int, int, OFF_T)>type = void *(*)(void *, SIZE_T, int, int, int, OFF_T)*__interception::real_mmap{void *(void *, SIZE_T, int, int, int, OFF_T)} 0x7ffff7b5aa20 <mmap64>type = void *(void *, SIZE_T, int, int, int, OFF_T)__interception::real_mmap0x7ffff7b5aa20 <mmap64>type = void *(*)(void *, SIZE_T, int, int, int, OFF_T)&__interception::real_mmap0x53ef00 <__interception::real_mmap>type = void *(**)(void *, SIZE_T, int, int, int, OFF_T)

10.2InitializeSignalInterceptors

初始化信号函数拦截器,如__interception::real_signal指向libcssignal__interception::real_sigaction指向libcsigaction

10.3 拦截剩下的函数

前面通用函数拦截器是各种Sanitizer均可能调用的,之后拦截strcat等ASAN特别需要拦截的函数。

10.4 关键点

大部分拦截的函数都使用真实的函数去执行了,也就是说没有插入额外的操作,主要是内存分配相关的函数被拦截并进入asan_xxx()的逻辑。

void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,AllocType alloc_type);void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type);void asan_delete(void *ptr, uptr size, uptr alignment,BufferedStackTrace *stack, AllocType alloc_type);void *asan_malloc(uptr size, BufferedStackTrace *stack);void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack);void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack);void *asan_reallocarray(void *p, uptr nmemb, uptr size,BufferedStackTrace *stack);void *asan_valloc(uptr size, BufferedStackTrace *stack);void *asan_pvalloc(uptr size, BufferedStackTrace *stack);void *asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack);int asan_posix_memalign(void **memptr, uptr alignment, uptr size,BufferedStackTrace *stack);uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp);uptr asan_mz_size(const void *ptr);void asan_mz_force_lock();void asan_mz_force_unlock();

11 CheckASLR、AndroidLogInit和ReplaceSystemMalloc

当前平台下不做任何事情。

12 DisableCoreDumperIfNecessary

依据disable_coredump设置RLIMIT_CORE的限长为0,也就是进制核心转储。

COMMON_FLAG(bool, disable_coredump, (SANITIZER_WORDSIZE == 64) && !SANITIZER_GO,"Disable core dumping. By default, disable_coredump=1 on 64-bit to avoid"" dumping a 16T+ core file. Ignored on OSes that don't dump core by"" default and for sanitizers that don't reserve lots of virtual memory.")

13 InitializeShadowMemory

查看内存映射布局,确保ShadowMemory不和Segment重叠,即当前整个ShadowMemory范围空闲可用。(如若ShadowMemory范围并不是全部可用,就需要引入MidMem)

打印地址空间布局:

|| `[0x10007fff8000, 0x7fffffffffff]` || HighMem |||| `[0x0fff7000, 0x10007fff7fff]` || HighShadow |||| `[0x00008fff7000, 0x0fff6fff]` || ShadowGap |||| `[0x00007fff8000, 0x00008fff6fff]` || LowShadow |||| `[0x000000000000, 0x00007fff7fff]` || LowMem||MemToShadow(shadow): 0x00008fff7000 0x000091ff6dff 0x004091ff6e00 0x0fff6fffredzone=16max_redzone=2048quarantine_size_mb=256Mthread_local_quarantine_size_kb=1024Kmalloc_context_size=30SHADOW_SCALE: 3SHADOW_GRANULARITY: 8SHADOW_OFFSET: 0x7fff8000

定址映射LowShadow和HighShadow并且不保留Swap空间(MAP_NORESERVE | MAP_FIXED),进一步不采用HugePage且不转储(MADV_NOHUGEPAGE | MADV_DONTDUMP)。

映射ShadowGap并禁止访问(PROT_NONE)。

14 AsanTSDInit

初始化tsd_key(Thread-Specifical Data Key),对应析构函数为PlatformTSDDtor

15 InstallDeadlySignalHandlers

为主线程创建备用的信号栈(当前初始化还在进行当中,调用真正的sigaltstack),大小为4* SIGSTKSZ=0x8000

然后依据sanitizer_flags.inc默认对SIGSEGV、SIGBUS、SIGFPE注册(消亡)信号处理函数AsanOnDeadlySignal

// compiler-rt/lib/sanitizer_common/sanitizer_flags.inc#define COMMON_FLAG_HANDLE_SIGNAL_HELP(signal) \"Controls custom tool's " #signal " handler (0 - do not registers the " \"handler, 1 - register the handler and allow user to set own, " \"2 - registers the handler and block user from changing it). "COMMON_FLAG(HandleSignalMode, handle_segv, kHandleSignalYes,COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGSEGV))COMMON_FLAG(HandleSignalMode, handle_sigbus, kHandleSignalYes,COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGBUS))COMMON_FLAG(HandleSignalMode, handle_abort, kHandleSignalNo,COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGABRT))COMMON_FLAG(HandleSignalMode, handle_sigill, kHandleSignalNo,COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGILL))COMMON_FLAG(HandleSignalMode, handle_sigtrap, kHandleSignalNo,COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGTRAP))COMMON_FLAG(HandleSignalMode, handle_sigfpe, kHandleSignalYes,COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGFPE))

16 InitializeAllocator(很重要又很复杂)

从ASAN_Flags和Common_Flags读取内存分配相关选项:

// compiler-rt/lib/asan/asan_allocator.hstruct AllocatorOptions {u32 quarantine_size_mb;u32 thread_local_quarantine_size_kb;u16 min_redzone;u16 max_redzone;u8 may_return_null;u8 alloc_dealloc_mismatch;s32 release_to_os_interval_ms;void SetFrom(const Flags *f, const CommonFlags *cf);void CopyTo(Flags *f, CommonFlags *cf);};// compiler-rt/lib/asan/asan_flags.incASAN_FLAG(int, quarantine_size_mb, -1,"Size (in Mb) of quarantine used to detect use-after-free ""errors. Lower value may reduce memory usage but increase the ""chance of false negatives.")ASAN_FLAG(int, thread_local_quarantine_size_kb, -1,"Size (in Kb) of thread local quarantine used to detect ""use-after-free errors. Lower value may reduce memory usage but ""increase the chance of false negatives. It is not advised to go ""lower than 64Kb, otherwise frequent transfers to global quarantine ""might affect performance.")ASAN_FLAG(int, redzone, 16,"Minimal size (in bytes) of redzones around heap objects. ""Requirement: redzone >= 16, is a power of two.")ASAN_FLAG(int, max_redzone, 2048,"Maximal size (in bytes) of redzones around heap objects.")// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.// /google/sanitizers/issues/131// /google/sanitizers/issues/309// TODO(glider,timurrrr): Fix known issues and enable this back.ASAN_FLAG(bool, alloc_dealloc_mismatch,!SANITIZER_MAC && !SANITIZER_WINDOWS && !SANITIZER_ANDROID,"Report errors on malloc/delete, new/free, new/delete[], etc.")// compiler-rt/lib/sanitizer_common/sanitizer_flags.incCOMMON_FLAG(bool, allocator_may_return_null, false,"If false, the allocator will crash instead of returning 0 on ""out-of-memory.")COMMON_FLAG(s32, allocator_release_to_os_interval_ms,((bool)SANITIZER_FUCHSIA || (bool)SANITIZER_WINDOWS) ? -1 : 5000,"Only affects a 64-bit allocator. If set, tries to release unused ""memory to the OS, but not more often than this interval (in ""milliseconds). Negative values mean do not attempt to release ""memory to the OS.\n")

基于选项初始化Allocator(包括quarantine和redzone等,此外会在0x640000000000~0x640000003000初始化RegionInfo,并污染这块区域,具体用faPoisonShadow0x00000c807fff8000~0x00000c807fff8600,这应该是第一次PoisonShadow?)

下面是一些备忘:

17 ASAN初始化大体完成

// compiler-rt/lib/asan/asan_rtl.cpp// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited// should be set to 1 prior to initializing the threads.asan_inited = 1;asan_init_is_running = false;

18 InitializeCoverage

设置不记录覆盖率。

使用__cxa_atexit注册__sanitizer_cov_dumpAtCxaAtexit使得在程序退出或共享库卸载时调用。

InternalDieCallbacks中注册消亡回调函数__sanitizer_cov_dump

InitTlsSize

调用_dl_get_tls_static_info获取tls_size

CreateMainThread

创建主线程上下文,与tsd_key绑定。然后针对主线程,通过查询proc_map和fs寄存器分别取得栈和TLS的地址区间,并将其标记为可寻址,PosionShadow为0。最后将主线程标记为运行中。

至此,初始化就完成了

如果觉得《ASAN Runtime【源码分析】(一)——初始化》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。