跳转到内容

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()

  1. define __ssp_bos(ptr) __builtin_object_size (ptr, __SSP_FORTIFY_LEVEL > 1)
  2. 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 会指示加载器使用共享库。

上一页: GNU C 编译器架构 堆栈保护 下一页: GEM 框架
华夏公益教科书