GNU C 编译器内部/堆栈保护 4 1
堆栈保护在以下文件中实现。
这些文件将 GIMPLE 翻译成 RTL。它们利用 CFG 来检测函数序言和结语。
1) cfgexpand.c
堆栈保护是在 expand_used_vars() 函数中创建的。根据堆栈保护标志,要么所有数组要么仅字符数组被首先分配。
tree_expand_cfg() 在调用 stack_protect_prologue() 时处理序言。相应的函数 stack_protect_epilogue() 从 calls.c 中调用以处理尾部调用,并从 tree_expand_cfg() 中调用;
2) function.c
stack_protect_prologue(), stack_protect_epilogue()
这些函数使用机器定义 targetm。
3) targhooks.c
包含目标架构钩子的默认初始化器。
default_stack_protect_guard() 构建一个 VAR_DECL 树节点,代表变量 __stack_chk_guard。
default_stack_protector_fail() 构建对函数 __stack_chk_fail() 的调用。
这些 AST 使用 expand_expr_stmt() 转换为 RTL 树。序言和结语函数也直接添加 RTL 表达式。例如,为了检测妥协,结语函数生成一个比较堆栈上的金丝雀字与初始值的条件语句
emit_cmp_and_jump_insns (x, y, EQ, NULL_RTX, ptr_mode, 1, label);
其中 x 和 y 是相应的声明 RTL
x = validize_mem (DECL_RTL (cfun->stack_protect_guard)); y = validize_mem (DECL_RTL (guard_decl));
堆栈保护实现了一定程度的静态分析,以确保具有固定长度参数的可疑函数没有漏洞。底层机制是内置函数。堆栈保护有文件 strcpy.h,它定义了感兴趣的函数,例如 strcpy()。新定义使用内置函数 __ssp_bos() 来找出对象大小,以及 __builtin___strcpy_chk()
- define __ssp_bos(ptr) __builtin_object_size (ptr, __SSP_FORTIFY_LEVEL > 1)
- define strcpy(dest, src) \
((__ssp_bos (dest) != (size_t) -1) \ ? __builtin___strcpy_chk (dest, src, __ssp_bos (dest)) \ : __strcpy_ichk (dest, src))
内置函数在编译时进行评估。如果目标缓冲区发生溢出,则在 maybe_emit_chk_warning() 中调用的函数 warning() 会打印一条消息。
如果堆栈保护无法静态地确定缓冲区是否发生溢出,则会在运行时调用函数 __strcpy_chk()。库 libssp 提供了该函数。但是,受保护的程序不需要显式地与该库进行链接。相反,GCC 会指示加载器使用共享库。