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

(LastCommitID=fed41342a82f5a3a9201819a82bf7a48313e296b)

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

0 关于ASAN初始化

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

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

// compiler-rt/lib/asan/asan_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 use this call as a trigger to wake up ASan from deactivated state.
void __asan_init() {
  AsanActivate();
  AsanInitInternal();
}

AsanInitInternal的逻辑包含如下:

1 CacheBinaryName

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 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.incasan_flags.inclsan_flags.incubsan_flags.inc初始化并(使用各自的Parser)注册各自的Flag结构体(内存分配操作用到了LowLevelAllocator::Allocate)。

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

确保 R e d z o n e S i z e ≥ 16 RedzoneSize\geq16 RedzoneSize16 R e d z o n e S i z e = 2 n RedzoneSize=2^n RedzoneSize=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;
  }
  ...
}

3 AsanCheckIncompatibleRT

记录当前进程内存映射布局(查看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.cpp
void 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 ...
}

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 useful "
    "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

H i g h M e m = 0 x 10007 f f f 8000 ∼ 0 x 7 f f f f f f f f f f f HighMem=0x10007fff8000\sim0x7fffffffffff HighMem=0x10007fff80000x7fffffffffff,对应(Shadow = (Mem >> 3) + 0x7fff8000)的 S h d o w M e m o r y = 0 x 02008 f f f 7000 ∼ 0 x 10007 f f f 7 f f f ShdowMemory=0x02008fff7000\sim0x10007fff7fff ShdowMemory=0x02008fff70000x10007fff7fff(与一致):

  • 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 一些简单配置

函数或操作内容
AddDieCallbackAsanDie注册为回调函数InternalDieCallbacks,在调用Die()时会调用。
SetCheckFailedCallbackAsanCheckFailed注册为回调函数CheckFailedCallback,在调用CheckFailed()时会调用。
SetPrintfAndReportCallbackAppendToErrorMessageBuffer注册为回调函数PrintfAndReportCallback,在调用CallPrintfAndReportCallback()时会调用。
__sanitizer_set_report_path由于common_flags_dont_use.log_path为空,因此默认输出路径仍为kStderrFd(2)
__asan_option_detect_stack_use_after_return = false不检测栈上的UAR问题。
SetLowLevelAllocateMinAlignment更新设置LowLevelAllocate最小对齐粒度,low_level_alloc_min_alignment=SHADOW_GRANULARITY(8),前面使用LowLevelAllocate时粒度为kLowLevelAllocatorDefaultAlignment
SetLowLevelAllocateCallbackOnLowLevelAllocate注册为回调函数low_level_alloc_callback,在调用LowLevelAllocator::Allocate()时会调用。

10 InitializeAsanInterceptors

10.1 InitializeCommonInterceptors

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_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>
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指向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    ||
|| `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
|| `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap  ||
|| `[0x00007fff8000, 0x00008fff6fff]` || LowShadow  ||
|| `[0x000000000000, 0x00007fff7fff]` || LowMem     ||
MemToShadow(shadow): 0x00008fff7000 0x000091ff6dff 0x004091ff6e00 0x02008fff6fff
redzone=16
max_redzone=2048
quarantine_size_mb=256M
thread_local_quarantine_size_kb=1024K
malloc_context_size=30
SHADOW_SCALE: 3
SHADOW_GRANULARITY: 8
SHADOW_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.h
struct 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.inc
ASAN_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.
// https://github.com/google/sanitizers/issues/131
// https://github.com/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.inc
COMMON_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,并污染这块区域,具体用faPoisonShadow 0x00000c807fff8000~0x00000c807fff8600,这应该是第一次PoisonShadow?)

下面是一些备忘:

变量类型
struct Allocator instancestruct __asan::Allocator
instance.allocator__sanitizer::CombinedAllocator<__sanitizer::SizeClassAllocator64<__asan::AP64<__sanitizer::LocalAddressSpaceView> >, __sanitizer::LargeMmapAllocatorPtrArrayDynamic>
instance.allocator.primary___sanitizer::SizeClassAllocator64<__asan::AP64<__sanitizer::LocalAddressSpaceView>
instance.allocator.secondary___sanitizer::LargeMmapAllocator<__asan::AsanMapUnmapCallback, __sanitizer::LargeMmapAllocatorPtrArrayDynamic, __sanitizer::LocalAddressSpaceView>

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。最后将主线程标记为运行中。

至此,初始化就完成了

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值