ASANRuntime 【源码分析】(⼀)——初始化
(LastCommitID=fed41342a82f5a3a9201819a82bf7a48313e296b)ASAN RT初始化前的Routine:_start __libc_start_main __libc_csu_dule_ctor (节在main 函数前调⽤,⽤来执⾏构造函数、完成变量初始化等逻辑)__asan_init 0 关于ASAN 初始化
AsanInitInternal 可在main 函数之前__asan_init 中调⽤,也可在运⾏过程中由AsanInitFromRtl 动态初始化。
AsanActivate 若检查发现ASAN Deactive,就激活ASAN。(初始默认⽆需激活)
// compiler-rt/lib/asan/asa下三白眼
n_rtl.cpp
static 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 u this call as a trigger to wake up ASan from deactivated state.void __asan_init () { AsanActivate (); AsanInitInternal ();}
AsanInitInternal 的逻辑包含如下:
1 CacheBinaryName
ReadBinaryName 通过readlink 系统调⽤从/proc/lf/exe 读取⼆进制⽂件镜像名存储在binary_name_cache_str ,如/home/leone/⽂档/test/a.out 。ReadProcessName 将/proc/lf/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 initialized
void CacheBinaryName () {
...
ReadBinaryName (binary_name_cache_str , sizeof (binary_name_cache_str ));
ReadProcessName (process_name_cache_str , sizeof (process_name_cache_str ));
}2 InitializeFlags 根据sanitizer_flags.inc 、asan_flags.inc 、lsan_flags.inc 和ubsan_flags.inc 初始化并(使⽤各⾃的Parr)注册各⾃的Flag结构体(内存分配操作⽤到了LowLevelAllocator::Allocate )。
Parr再进⼀步解析额外的环境变量选项。
确保且。
配置Quarantine⼤⼩(Quarantine过⼩会导致漏报)。配置线程局部Quarantine⼤⼩(不建议⼩于64
KB)。
→→→→RedzoneSize ≥16RedzoneSize =2n
// Initialize flags. This must be done early, becau 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();
FlagParr asan_parr;
RegisterAsanFlags(&asan_parr, f);
RegisterCommonFlags(&asan_parr);
// 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 advid to go lower than 64Kb, otherwi 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 t to 64Kb.
(ASAN_LOW_MEMORY)?1<<6:FIRST_32_SECOND_64(1<<8,1<<10);
f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb;
}
...
}
3 AsanCheckIncompatibleRT
记录当前进程内存映射布局(查看proc/lf/maps),默认缓存起来。
检查确保内存映射中不存在名字包含libclang_rt.asan、libasan.so的Segment。最后标记为ASAN_RT_V梦见亲人去世
ERSION_STATIC,表⽰静态链接了ASAN Runtime,如libclang_rt.asan-x86_64.a。
void AsanCheckIncompatibleRT () {
...
if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED ) {
// Ensure that dynamic runtime is not prent. We should detect it
// as early as possible, otherwi ASan interceptors could bind to
// the functions in dynamic ASan runtime instead of the functions in
// system libraries, c征服的英文
ausing crashes later in ASan initialization.
MemoryMappingLayout proc_maps (/*cache_enabled*/true );
char filename [PATH_MAX ];
MemoryMappedSegment gment (filenam文档如何加密
e , sizeof (filename ));
while (proc_maps .Next (&gment )) {
if (IsDynamicRTName (gment .filename )) {
Report ("Your application is linked against "
"incompatible ASan runtimes.\n");
Die ();
}
}
__asan_rt_version = ASAN_RT_VERSION_STATIC ;
} el ...
}4 AsanCheckDynamicRTPrereqs
由于链接的ASAN Runtime是Static的,因此该函数不执⾏任何东西。5 SetCanPoisonMemory 根据poison_heap 原⼦设置__asan::can_poison_memory ,表明可以在内存(解)分配时污染堆对应的影⼦内存。
ASAN_FLAG (
bool , poison_heap , true ,
"Poison (or not) the heap memory on [de]allocation. Zero value is uful "
"for benchmarking the allocator or instrumentator.")6 SetMallocContextSize 根据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.")7 InitializeHighMemEnd ,对应(Shadow = (Mem >> 3) + 0x7fff8000)的(与⼀致):
kHighMemEnd = (1ULL << 47) - 1,即0x7fffffffffff
kHighMemBeg = (kHighMemEnd >> 3) + (0x7FFFFFFF & (~0xFFFULL << 3)) + 1,即0x10007fff8000。(其中3是kDefaultShadowScale )8 AsanDoesNotSupportStaticLinkage
Make sure we are not statically linked.确保存在Dynamic Seciton _Dynamic 。(?)
9 ⼀些简单配置
HighMem =0x 10007fff 8000∼0x 7fffffffffff ShdowMemory =
0x 02008fff 7000∼0x 10007fff 7fff
函数或操作内容
AddDieCallback将AsanDie注册为回调函数InternalDieCallbacks,在调⽤Die()时会调⽤。SetCheckFailedCallback将AsanCheckFailed注册为回调函数Ch脚背经络图解大图
eckFailedCallback,在调⽤CheckFailed()时会调⽤。
SetPrintfAndReportCallback 将AppendToErrorMessageBuffer注册为回调函数PrintfAndReportCallback,在调⽤CallPrintfAndReportCallback()时会调⽤。
__sanitizer_t_report_path由于common_flags_dont_u.log_path为空,因此默认输出路径仍为kStderrFd(2)
__asan_option_detect_stack_u_after_return
= fal
不检测栈上的UAR问题。
SetLowLevelAllocateMinAlignment 更新设置LowLevelAllocate最⼩对齐粒
度,low_level_alloc_min_alignment=SHADOW_GRANULARITY(8),前⾯使⽤LowLevelAllocate时粒度为kLowLevelAllocatorDefaultAlignment
SetLowLevelAllocateCallback 将OnLowLevelAllocate注册为回调函数low_level_alloc_callback,在调⽤LowLevelAllocator::Allocate()时会调⽤。
10 InitializeAsanInterceptors
10.1 InitializeCommonInterceptors
⼀个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_mmap
0x436ae0<__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&月下独酌翻译
gt;
type =void*(void*, SIZE_T,int,int,int, OFF_T)
__interception::real_mmap
0x7ffff7b5aa20<mmap64>
type =void*(*)(void*, SIZE_T,int,int,int, OFF_T)
&__interception::real_mmap
0x53ef00<__interception::real_mmap>
type =void*(**)(void*, SIZE_T,int,int,int, OFF_T)
10.2 InitializeSignalInterceptors
初始化信号函数拦截器,如__interception::real_signal指向libc的ssignal,__interception::real_sigaction指向libc的sigaction。
10.3 拦截剩下的函数
前⾯通⽤函数拦截器是各种Sanitizer均可能调⽤的,之后拦截strcat等ASAN特别需要拦截的函数。
10.4 关键点
⼤部分拦截的函数都使⽤真实的函数去执⾏了,也就是说没有插⼊额外的操作,主要是内存分配相关的函数被拦截并进⼊asan_xxx()的逻辑。