(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.inc
、asan_flags.inc
、lsan_flags.inc
和ubsan_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.asan
、libasan.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
,即0x7fffffffffff
kHighMemBeg = (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_mmap
、mmap
、__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
指向libc
的ssignal
,__interception::real_sigaction
指向libc
的sigaction
。
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,并污染这块区域,具体用fa
值PoisonShadow
0x00000c807fff8000~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_dump
和AtCxaAtexit
使得在程序退出或共享库卸载时调用。
在InternalDieCallbacks
中注册消亡回调函数__sanitizer_cov_dump
。
InitTlsSize
调用_dl_get_tls_static_info
获取tls_size
。
CreateMainThread
创建主线程上下文,与tsd_key
绑定。然后针对主线程,通过查询proc_map和fs寄存器分别取得栈和TLS的地址区间,并将其标记为可寻址,PosionShadow为0。最后将主线程标记为运行中。
至此,初始化就完成了
如果觉得《ASAN Runtime【源码分析】(一)——初始化》对你有帮助,请点赞、收藏,并留下你的观点哦!