canary是Linux为防止栈溢出的一种保护机制,接着我们分析glibc对canary的实现过程,首先给出跟canary相关的调用栈:
- security_init() //在elf/rtld.c中
- dl_main() 在elf/rtld.c中
- _dl_sysdep_start() 在elf/dl-sysdep.c中
- _dl_start_final() 在rtld.c中
- _dl_start() 在rtld.c中
- _start() /lib/ld-linux.so.2
static void security_init (void) { /* Set up the stack checker's canary. */
//一般情况下此时_dl_random的值是由kernel写入的,glibc直接使用了里面的值, uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); //把canary的最后一位设为'x00' #ifdef THREAD_SET_STACK_GUARD
//TLS会进入这里.macro的定义及其详细展开见下面 THREAD_SET_STACK_GUARD (stack_chk_guard); #else __stack_chk_guard = stack_chk_guard; #endif /* Set up the pointer guard as well, if necessary. */ uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random, stack_chk_guard); #ifdef THREAD_SET_POINTER_GUARD THREAD_SET_POINTER_GUARD (pointer_chk_guard); //有TLS结构就会进入这里 #endif __pointer_chk_guard_local = pointer_chk_guard; /* We do not need the _dl_random value anymore. The less information we leave behind, the better, so clear the variable. */ _dl_random = NULL; }
THREAD_SET_STACK_GUARD()宏的定义
#define THREAD_SET_STACK_GUARD(VALUE) THREAD_SETMEM(THREAD_SELF, header.stack_guard, value)
header的定义,TLS相关的数据结构,注意元素stack_guard的偏移是20也就是0x14
typedef struct { void *tcb; /* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */ dtv_t *dtv; void *self; /* Pointer to the thread descriptor. */ int multiple_threads; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; int gscope_flag; #ifndef __ASSUME_PRIVATE_FUTEX int private_futex; #else int __unused1; #endif /* Reservation of some values for the TM ABI. */ void *__private_tm[4]; /* GCC split stack support. */ void *__private_ss; } tcbhead_t;
将cananry的值写入gs:0x14,此处gs寄存器指向TLS结构体,gs:0x14就是将canary写入结构体中偏移为0x14处,也就是写入stack_guard中
/* Same as THREAD_SETMEM, but the member offset can be non-constant. */ # define THREAD_SETMEM(descr, member, value) ({if (sizeof (descr->member) == 4) asm volatile ("movl %0,%%gs:%P1" : : "ir" (value),
"i" (offsetof (struct pthread, member))); })
生成TLS结构体的函数位于glibc-2.23/elf/dl-tls.c,可以看到,程序事实上是调用了__libc_memalign函数来分配内存,而__libc_memalign函数最终调用的是mmap函数。如果程序定义了THREAD_SET_STACK_GUARD则canary会被放在TLS,否则canary会被放在.bss中。