为什么地址消毒剂对 bss 全局溢出不起作用?

Why address sanitizer doesn't work for bss global overflow?

我做了什么

测试1

  1 #include <stdio.h>                                                              
  2                                                                                 
  3 int test[16];                                                                   
  4                                                                                 
  5 int main()                                                                      
  6 {                                                                               
  7     test[17] = -1;                                                              
  8 } 

/tmp $ gcc ./main.c -o main -fsanitize=address
/tmp $ ./main 
/tmp $

测试2

  1 #include <stdio.h>                                                              
  2                                                                                 
  3 int test[16] = {1};                                                             
  4                                                                                 
  5 int main()                                                                      
  6 {                                                                               
  7     test[17] = -1;                                                              
  8 }

 /tmp $ gcc ./main.c -o main -fsanitize=address
 /tmp $ ./main 

=================================================================
==19776==ERROR: AddressSanitizer: global-buffer-overflow on address 
...

看起来全局缓冲区溢出检测对放置在 bss 中的全局变量不起作用(是吗?)。这背后的原因是什么?

更新:

存储的代码没有优化掉。 系统信息:

$ gcc --version
gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

这是在FAQ:

Q: Why didn't ASan report an obviously invalid memory access in my code?

A1: If your errors is too obvious, compiler might have already optimized it out by the time Asan runs.

A2: Another, C-only option is accesses to global common symbols which are not protected by Asan (you can use -fno-common to disable generation of common symbols and hopefully detect more bugs).

您的案例可能属于 A2,因此添加 -fno-common 应该会有所帮助。

常见符号(默认情况下为零初始化全局变量生成)的问题在于,由于它们古怪的遗留语义,Asan 无法为它们插入 redzones(参见 GCC #55739 了解详细信息).通过提供 -fno-common 您可以禁用公共资源的生成,而是要求 GCC 在所有情况下生成正常的全局符号(这有很小的机会破坏依赖于公共符号行为的错误编写的程序,但通常这不是问题)。

其他工具,包括我们的(语义设计)CheckPointer 工具,可以执行此操作。 (编辑输出以删除一些不相关的文本):

对于 test1.c 正如 OP 定义的那样:

c:\DMS\Domains\C\GCC4\Tools\CheckPointer\Example\Source>DMSCheckPointer C~GCC4 test1.c
C~GCC4 CheckPointer Version 1.2.1001
Copyright (C) 2011-2016 Semantic Designs, Inc; All Rights Reserved; SD Confidential

c:\DMS\Domains\C\GCC4\Tools\CheckPointer\Example\Source>gcc.exe -I"c:\DMS\Domains\C\GCC4\Tools\CheckPointer" -I.\Target -otest1.exe Target\test1.c <snip>

test1
*** Error: CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
       Dereference of pointer is out of bounds.
in function: main, line: 7, file c:\DMS\Domains\C\GCC4\Tools\CheckPointer\Example\Source\test1.c

对于test2.c:

c:\DMS\Domains\C\GCC4\Tools\CheckPointer\Example\Source>DMSCheckPointer C~GCC4 test2.c
C~GCC4 CheckPointer Version 1.2.1001
Copyright (C) 2011-2016 Semantic Designs, Inc; All Rights Reserved; SD Confidential

c:\DMS\Domains\C\GCC4\Tools\CheckPointer\Example\Source>gcc.exe -I"c:\DMS\Domains\C\GCC4\Tools\CheckPointer" -I.\Target -otest2.exe Target\test2.c <snip>

test2
*** Error: CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
       Dereference of pointer is out of bounds.
in function: main, line: 7, file c:\DMS\Domains\C\GCC4\Tools\CheckPointer\Example\Source\test2.c

CheckPointer 不需要 "redzones" 数据;它根据语言语义检查访问。这意味着它会,例如,检测结构中任何地方的 char 数组末尾的访问,无论该结构是本地(堆栈)变量、堆中、线程本地存储中还是嵌入在其他结构中。

与"CWE-119"相关的"odd phrasing"不是我们选择的词,它是由Common Weakness Enumeration的标准定义的。 CheckPointer 使用 CWE 定义、分类和描述报告错误。