更新时间:2026-07-28 GMT+08:00
分享

C/C++应用程序编译规范

在C/C++应用程序开发过程中,编译器类型、语言标准及编译选项的差异,可能引入未定义行为、安全隐患和平台兼容性问题,进而影响系统稳定性和产品交付质量。本规范对C/C++应用程序的编译器选型、语言标准、编译参数、构建模式及相关管理要求作出统一规定,作为项目开发、集成、测试和交付过程中的基本遵循依据。

如果需要定位C/C++应用程序中出现的内存问题,可以开启ASan(AddressSanitizer)并解析ASan日志定位问题原因,详细请参考ASan以及ASan日志解析

ASan

ASan(AddressSanitizer)是一款用于C/C++的内存错误检测器。以下内容介绍如何使用ASan日志查看内存问题。

ASan可以获取的内存错误信息,请参见表1

表1 Asan报错信息关键字

关键字

含义

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

内存泄漏

该工具支持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"

    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日志会生成在2ASan环境变量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/workspace/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名称。
  • 错误类型。
  • 一个“代表性位置”(模块+偏移、以及符号化后的函数名)。

简要归纳可以快速定位“主要出错点”。

相关文档