
# C/C++应用程序编译规范
在C/C++应用程序开发过程中，编译器类型、语言标准及编译选项的差异，可能引入未定义行为、安全隐患和平台兼容性问题，进而影响系统稳定性和产品交付质量。本规范对C/C++应用程序的编译器选型、语言标准、编译参数、构建模式及相关管理要求作出统一规定，作为项目开发、集成、测试和交付过程中的基本遵循依据。
如果需要定位C/C++应用程序中出现的内存问题，可以开启ASan（AddressSanitizer）并解析ASan日志定位问题原因，详细请参考[ASan]以及[ASan日志解析]。
 #### ASan
ASan（AddressSanitizer）是一款用于C/C++的内存错误检测器。以下内容介绍如何使用ASan日志查看内存问题。
ASan可以获取的内存错误信息，请参见[表1]。
 表1Asan报错信息关键字 
| 关键字                      | 含义                  |
|:---|:---|
| heap-buffer-overflow     | 堆越界                 |
| heap-use-after-free      | 释放后使用               |
| double-free              | 重复释放                |
| invalid-free             | 非法释放                |
| stack-buffer-overflow    | 栈越界                 |
| stack-use-after-return   | 栈UAR（函数返回后仍然使用其栈内存） |
| global-buffer-overflow   | 全局区越界               |
| alloc-dealloc-mismatch   | 分配释放不匹配             |
| new-delete-type-mismatch | new/delete 错误       |
| use-after-poison         | 访问红区                |
| memory-leak              | 内存泄漏                |
   
![](https://support.huaweicloud.com/centralized-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
该工具支持x86、ARMCPU架构，支持Linux操作系统。
#### 开启并生成ASan日志
以下步骤详细介绍如何开启ASan，并生成ASan日志：
1. 使用ASan，需要在编译C/C++程序时在编译命令中添加-fsanitize=address。 
   以最基础的编译命令举例：
   ```
   gcc test.c -o test -fsanitize=address
   ```
   
   
2. 检查ASan环境变量是否设置。
   
   ```
   echo $ASAN_OPTIONS
   ```
   如果输出为空，执行[3]。否则检查Asan环境变量是否含有目标参数，并且参数值设置正确。
   
   
3. 设置Asan环境变量。
   
   执行以下命令设置ASan环境变量：
   ```
   export ASAN_OPTIONS="unmap_shadow_on_exit=true:abort_on_error=true:log_path=[log_path]/[log_file_prefix]:alloc_dealloc_mismatch=1:fast_unwind_on_fatal=true:detect_stack_use_after_return=1:log_exe_name=true"
   ```
   ![](https://support.huaweicloud.com/centralized-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
   ASAN_OPTIONS可设置参数请参考。
   - unmap_shadow_on_exit：在退出时显式地取消映射（大页）影子内存。默认为false。
   
   - abort_on_error：在打印错误报告后，工具将调用abort()函数，而不是_exit()函数。默认为false。
   
   - log_path：将日志写入"log_path.pid"。特殊值有"stdout"和"stderr"，默认为"stderr"。 其中环境变量中建议设置log_path，该参数为ASan日志输出路径，如不设置，无法获取具体的报错栈。\[log_path\]为日志文件目录，\[log_file_prefix\]为日志文件前缀。\[log_path\]和\[log_file_prefix\]均可由用户设置。
     
   
   - alloc_dealloc_mismatch：用来检测"分配 / 释放方式不匹配"的内存错误，默认为0。
   
   - fast_unwind_on_fatal：在发生致命错误时，使用基于快速帧指针的异常处理程序。默认为false。
   
   - detect_stack_use_after_return：用于检测"函数返回后仍然使用其栈内存"的错误（Stack Use-After-Return，简称 UAR），默认为0。
   
   - log_exe_name：报告错误时提及可执行文件名称，并在日志中添加可执行文件名（如"log_path.exe_name.pid"）。
    
   
   
4. 运行编译后的文件。 
   ```
   ./test
   ```
   
   
5. 查看ASan日志 
   执行结束后，ASan日志会生成在[2]ASan环境变量ASAN_OPTIONS中log_path设置的路径\[log_path\]中，日志格式为\[log_file_prefix\].\[exe_name\].\[pid\]。
   以log_path设置为"/var/log"，log_file_prefix设置为"runlog"，exe_name为可执行文件名为test，运行test时的pid为1234为例，日志文件输出路径为：
   ```
   /var/log/runlog.test.1234
   ```
   
   
 
 #### ASan日志解析
下面通过一段ASan日志分析日志结构：
```
=================================================================
==602791==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000026904 at pc 0x000000400775 bp 0x7ffeba71f610 sp 0x7ffeba71f608
WRITE of size 4 at 0x603000026904 thread T0
    #0 0x400774 in main (/home/test/workspace/mem+0x400774)
    #1 0x7fb441858964 in __libc_start_main (/lib64/libc.so.6+0x22964)
    #2 0x400658  (/home/test/workspace/mem+0x400658)
0x603000026904 is located 0 bytes to the right of 20-byte region [0x6030000268f0,0x603000026904)
allocated by thread T0 here:
    #0 0x7fb4428d84d0 in __interceptor_malloc ../../.././libsanitizer/asan/asan_malloc_linux.cc:62
    #1 0x400731 in main (/home/test/workspacet/mem+0x400731)
    #2 0x7fb441858964 in __libc_start_main (/lib64/libc.so.6+0x22964)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/test/workspace/mem+0x400774) in main
Shadow bytes around the buggy address:
  0x0c067fffccd0: 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00 00 fa
  0x0c067fffcce0: fa fa 00 00 00 00 fa fa 00 00 00 00 fa fa fd fd
  0x0c067fffccf0: fd fd fa fa fd fd fd fd fa fa 00 00 00 fa fa fa
  0x0c067fffcd00: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fd
  0x0c067fffcd10: fa fa fd fd fd fd fa fa 00 00 00 fa fa fa 00 00
=>0x0c067fffcd20:[04]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fffcd30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fffcd40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fffcd50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fffcd60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fffcd70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==602791==ABORTING
```
从上到下拆分日志依次解析：
日志头部信息：
```
==602791==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000026904 at pc 0x000000400775 bp 0x7ffeba71f610 sp 0x7ffeba71f608
```
字段含义：
- ==602791== 进程 ID（PID）。ASan用这种格式标识输出来自哪个进程（多进程时作用明显）。
  
- ERROR： 日志级别/事件类型（通常是 ERROR）。
  
- AddressSanitizer 产生该报告的sanitizer名称。
  
- heap-buffer-overflow 错误类型关键字，详情请参考[表1]。
  
- on address 0x... 出问题的内存地址（被访问的目标地址）。
  
- at pc 0x... 程序计数器（PC）：发生访问时的指令地址（可用于符号化到函数/行号）。
  
- bp 0x... sp 0x... 寄存器快照：base pointer / stack pointer（用于定位栈帧、调试）。
  

- WRITE of size 4 at 0x603000026904 thread T0 主线程在向堆内存地址写入4字节数据时发生了非法访问，其中thread T0表示线程T0， 即主线程。
  
访问动作摘要如下：
```
WRITE of size 4 at 0x603000026904 thread T0
```
字段含义：
- WRITE/READ 访问类型：写或读。
  

- of size 4 访问大小（字节数）。
  
- at 0x... 再次给出被访问地址（与首行一致）。
  
- thread T0 线程标识：T0通常是主线程；多线程程序会有T1/T2...。
  
报错触发调用栈如下：
```
#0 0x400774 in main (/home/test/workspace/mem+0x400774)    
#1 0x7fb441858964 in __libc_start_main (/lib64/libc.so.6+0x22964)    
#2 0x400658  (/home/test/workspace/mem+0x400658)
```
地址与对象边界描述如下：
```
0x603000026904 is located 0 bytes to the right of 20-byte region [0x6030000268f0,0x603000026904)
```
- is located ... 目标地址相对某个已知内存块的相对位置（例如：在右边界外0字节、左边界外为多少字节、位于块内部偏移多少字节等）。
  
- xx-byte region 相关内存块的大小。
  
- \[start, end) 该内存块的起止地址区间（左闭右开是常用表示法）。
  
分配/释放来源：
```
allocated by thread T0 here:
    #0 0x... in __interceptor_malloc ...:62
    #1 0x... in main (...)
```
- allocated by thread T0 here 说明内存是在哪个线程、通过哪次分配得到的，并给出分配时栈回溯。
  
- #0 ... __interceptor_malloc 拦截器函数（interceptor）：ASan会把 malloc/free/new/delete等替换/包装成自己的版本，用来记录元数据与检测越界。
  asan_malloc_linux.cc:62是ASan运行库源码位置（用于表明这是ASan的拦截实现）。
  若是use-after-free，通常还会出现 freed by thread ... here: 的释放栈。
  
SUMMARY行：简要归纳（Summary）：
```
SUMMARY: AddressSanitizer: heap-buffer-overflow (/path/to/bin+0x400774) in main
```
通常包含：
- sanitizer名称。
- 错误类型。
- 一个"代表性位置"（模块+偏移、以及符号化后的函数名）。
简要归纳可以快速定位"主要出错点"。
