如何在生产环境中追踪运行态内存崩溃的第一现场

引言

在c/c++开发中,内存相关错误一直都是开发人员的高压线,由于近期工作中上线服务偶尔出现崩溃问题,而崩溃的位置竟然是在 malloc函数的内部(见下图1),看到崩溃的第一反应是自己的程序出了问题,glibc使用多年还是很稳定的。于是先进行了一波代码自查,检查了程序中所有的memcpy、strcpy、memset、sprintf函数的使用并未发现异常,于是放弃人工检查,改为使用工具来进行定位分析。

工具选择

在众多内存检测工具中,各有优缺点,需要结合实际情况来选择定位,

常见的内存错误有以下几种类型

memory overrun 内存溢出
double free 同一内存多次释放
use after free 内存释放后再次使用
wild free 释放内存的参数为非法值
access uninitialized memory 访问未初始化内存
memory leak 内存泄露
use after return 函数返回栈内指针
stack overflow 栈溢出

常规的内存错误检测工具及其支持的功能对比

memory overrun double free use after free wild free access uninited read invalid memory memory leak use after return stack overflow thread safe Performance
Memory checking tools in Glibc Yes Yes Yes Yes(if use memcpy, strcpy, etc)
TCMalloc(Gperftools) Yes Yes
Valgrind Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes 官方文档说明 为 40X
Address Sanitizer(ASan) Yes Yes Yes Yes (Memory Sanitizer) Yes Yes Yes Yes Yes 官方文档说明 2x,平均1.8x
Memwatch Yes Yes NO
Dr.Memory Yes Yes Yes Yes Yes Yes Yes Yes
Electric Fence Yes Yes Yes Yes NO
Dmalloc Yes Yes Yes Yes Yes Yes

我们的需求

  • 线程安全

  • 测试很难复现,需要线上运行程序,所以性能差异不要太大

  • 需要尽可能多的检测出相关未知内存错误

从以上对比分析中来看Address Sanitizer(ASan) 是可以比较全面的检测发现内存等相关问题,且运行时性能损失较小,因此重点研究了Address Sanitizer 的相关用法,其他的tcmalloc和MALLOC_CHECK_ 等也做了相关功能对比测试,Address Sanitizer 确实对各种内存相关问题检测比较全面,且对程序运行时的性能影响相对较小,官方说2x的性能下降,因此决定使用Address Sanitizer来进行相关异常的定位检测。

Address Sanitizer的使用研究

介绍

Sanitizer 是google开发的一套检测工具,包含AddressSanitizer, MemorySanitizer, ThreadSanitizer, LeakSanitizer 等,其中Address Sanitizer主要是用于检测地址寻址问题。主要通过libasan动态库在程序执行前替换系统的malloc函数来实现相关地址的检测功能。

Sanitizer 早先是LLVM中的特性,后被加入GCC 4.8中,在GCC 4.9后加入对ARM平台的支持。因此用时不需要第三方库,通过在编译时指定flag即可打开开关。因为gcc 4.8的Address Sanitizer还不完善,没有符号信息,所以最好使用gcc4.9以后的版本。

Sanitizer 项目地址: https://github.com/google/sanitizers

使用 Address Sanitizer 的注意点

  • 要使用系统自带的内存管理库,不能使用第三方的内存管理库,因为这个功能要拦截malloc,free等标准函数。
  • 虽然Address Sanitizer是gcc的一部分,但默认是没有安装的,所以需要安装 libasan
  • 不同版本的gcc使用不同版本的libasan, gcc版本和libasan版本对应关系见下图
  • gcc4.8版对应的libasan不支持官方wiki中的相关动态参数,且没有符号信息
gcc版本号 libasan版本名
4.8/4.9 libasan-x.x
5.x libasan2-x.x
6.x libasan3-x.x
7.x libasan4-x.x
8.x libasan5-x.x

如何在程序中使用Sanitizer

Sanitizer通用参数可参考 https://github.com/google/sanitizers/wiki/SanitizerCommonFlags
AddressSanitizer参数可参考 https://github.com/google/sanitizers/wiki/AddressSanitizerFlags

  1. 在gcc或g++的编译选项中增加 -fsanitize=address -fno-omit-frame-pointer 编译选项以开启sanitize的使用

    1
    2
    3
    -fsanitize=address    #开启地址越界检查功能
    -fno-omit-frame-pointer #开启后,可以打印详细的堆栈信息和符号表信息
    如:g++ -g -fsanitize=address -fno-omit-frame-pointer main.cpp -o test
  2. 在程序运行时指定 sanitize 依赖的环境变量 ASAN_OPTIONS 如下:

    1
    2
    3
    4
    5
    	如:export 	ASAN_OPTIONS=abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1:log_path=/tmp/proxy_ng_err
    abort_on_error=1 # 当遇到错误时调用abort而不是_exit来退出程序。默认为0,调用_exit
    disable_coredump=0 # 0 开启检测异常时生成coredump文件;若要生效还需配合系统设置 ulimit -c unlimited; 默认为1,禁用coredump ,
    unmap_shadow_on_exit=1 # 是否在退出时调用unmap解除内存映射,默认0 不解除
    log_path=/tmp/proxy_ng_err # 将日志写入/tmp/proxy_ng_err.pid,pid为进程号, 默认输出至标准错误(strerr)
  3. 官方文档说在程序代码中内嵌__asan_default_options 函数的实现,返回ASAN_OPTIONS期望设置的参数也可以起到类似设置环境变量的作用。
    此方法本人在gcc4/gcc5/gcc6/gcc8中分别做了验证,各个环境下相关设置均未生效:(

如何验证当前编译程序是否开启了Address Sanitizer检测

  1. 编写如下测试程序main2.cpp, 并使用如下命令生成可执行文件test_oom
    g++ -g -fsanitize=address -fno-omit-frame-pointer main2.cpp -o test_oom

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    /**
    * Copyright (C) 2015-2018 IQIYI
    * All rights reserved.
    *
    * Author : 王海斌
    * E-mail : wanghaibin@qiyi.com
    * Version :
    * Date : Thu 02 Jan 2020 09:45:24 PM CST
    * Destription: 验证asan检测
    */

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <unistd.h>

    // 测试sprintf内存越界
    void test_oom_sprintf()
    {
    char buf[5]={0};
    const char * dd="123456";
    sprintf(buf, "%s", dd);
    }

    //测试memcpy内存越界
    void test_oom_memcpy()
    {
    char buf[5]={0};
    const char * dd="123456";
    memcpy(buf, dd, strlen(dd));
    }


    // 测试数组访问越界
    void test_oom_array(int argc)
    {
    int *array = new int[100];
    array[0] = 0;
    int res = array[argc + 100]; // BOOM
    delete [] array;
    for(;;) {
    printf("sss\n");
    sleep(1);
    }
    }

    int main(int argc, char **argv) {
    test_oom_sprintf();
    return 0;
    }

  2. 执行ldd test_oom 查看依赖库,如下图包含对libasan的引用则说明已启用
    a. gcc 4.8或4.9编译连接库如下:

    gcc 5.x编译连接库如下图

    gcc 8.x 编译连接库如下图:

  3. 如何验证执行程序是否是否支持Address Sanitizer相关动态参数的设置
    gcc4.x 执行help信息, 在gcc4.8下不支持相关参数,所以没有任何help相关信息,如下图

    gcc 5.x编译后的支持动态参数如下图:

    先显示help信息,然后才输出检测的异常信息,其中help信息显示支持的动态参数信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    [root@jiyang-cnc-10-153-56-72 err]# ASAN_OPTIONS=help=1 ./test_oom 
    Available flags for AddressSanitizer:
    quarantine_size
    - Deprecated, please use quarantine_size_mb.
    quarantine_size_mb
    - Size (in Mb) of quarantine used to detect use-after-free errors. Lower value may reduce memory usage but increase the chance of false negatives.
    thread_local_quarantine_size_kb
    - Size (in Kb) of thread local quarantine used to detect use-after-free errors. Lower value may reduce memory usage but increase the chance of false negatives. It is not advised to go lower than 64Kb, otherwise frequent transfers to global quarantine might affect performance.
    redzone
    - Minimal size (in bytes) of redzones around heap objects. Requirement: redzone >= 16, is a power of two.
    max_redzone
    - Maximal size (in bytes) of redzones around heap objects.
    debug
    - If set, prints some debugging information and does additional checks.
    report_globals
    - Controls the way to handle globals (0 - don't detect buffer overflow on globals, 1 - detect buffer overflow, 2 - print data about registered globals).
    check_initialization_order
    - If set, attempts to catch initialization order issues.
    replace_str
    - If set, uses custom wrappers and replacements for libc string functions to find more errors.
    replace_intrin
    - If set, uses custom wrappers for memset/memcpy/memmove intrinsics.
    detect_stack_use_after_return
    - Enables stack-use-after-return checking at run-time.
    min_uar_stack_size_log
    - Minimum fake stack size log.
    max_uar_stack_size_log
    - Maximum fake stack size log.
    uar_noreserve
    - Use mmap with 'noreserve' flag to allocate fake stack.
    max_malloc_fill_size
    - ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes that will be filled with malloc_fill_byte on malloc.
    max_free_fill_size
    - ASan allocator flag. max_free_fill_size is the maximal amount of bytes that will be filled with free_fill_byte during free.
    malloc_fill_byte
    - Value used to fill the newly allocated memory.
    free_fill_byte
    - Value used to fill deallocated memory.
    allow_user_poisoning
    - If set, user may manually mark memory regions as poisoned or unpoisoned.
    sleep_before_dying
    - Number of seconds to sleep between printing an error report and terminating the program. Useful for debugging purposes (e.g. when one needs to attach gdb).
    sleep_after_init
    - Number of seconds to sleep after AddressSanitizer is initialized. Useful for debugging purposes (e.g. when one needs to attach gdb).
    check_malloc_usable_size
    - Allows the users to work around the bug in Nvidia drivers prior to 295.*.
    unmap_shadow_on_exit
    - If set, explicitly unmaps the (huge) shadow at exit.
    protect_shadow_gap
    - If set, mprotect the shadow gap
    print_stats
    - Print various statistics after printing an error message or if atexit=1.
    print_legend
    - Print the legend for the shadow bytes.
    print_scariness
    - Print the scariness score. Experimental.
    atexit
    - If set, prints ASan exit stats even after program terminates successfully.
    print_full_thread_history
    - If set, prints thread creation stacks for the threads involved in the report and their ancestors up to the main thread.
    poison_heap
    - Poison (or not) the heap memory on [de]allocation. Zero value is useful for benchmarking the allocator or instrumentator.
    poison_partial
    - If true, poison partially addressable 8-byte aligned words (default=true). This flag affects heap and global buffers, but not stack buffers.
    poison_array_cookie
    - Poison (or not) the array cookie after operator new[].
    alloc_dealloc_mismatch
    - Report errors on malloc/delete, new/free, new/delete[], etc.
    new_delete_type_mismatch
    - Report errors on mismatch between size of new and delete.
    strict_init_order
    - If true, assume that dynamic initializers can never access globals from other modules, even if the latter are already initialized.
    start_deactivated
    - If true, ASan tweaks a bunch of other flags (quarantine, redzone, heap poisoning) to reduce memory consumption as much as possible, and restores them to original values when the first instrumented module is loaded into the process. This is mainly intended to be used on Android.
    detect_invalid_pointer_pairs
    - If >= 2, detect operations like <, <=, >, >= and - on invalid pointer pairs (e.g. when pointers belong to different objects); If == 1, detect invalid operations only when both pointers are non-null.
    detect_container_overflow
    - If true, honor the container overflow annotations. See https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
    detect_odr_violation
    - If >=2, detect violation of One-Definition-Rule (ODR); If ==1, detect ODR-violation only if the two variables have different sizes
    suppressions
    - Suppressions file name.
    halt_on_error
    - Crash the program after printing the first error report (WARNING: USE AT YOUR OWN RISK!)
    use_odr_indicator
    - Use special ODR indicator symbol for ODR violation detection
    allocator_frees_and_returns_null_on_realloc_zero
    - realloc(p, 0) is equivalent to free(p) by default (Same as the POSIX standard). If set to false, realloc(p, 0) will return a pointer to an allocated space which can not be used.
    verify_asan_link_order
    - Check position of ASan runtime in library list (needs to be disabled when other library has to be preloaded system-wide)
    symbolize
    - If set, use the online symbolizer from common sanitizer runtime to turn virtual addresses to file/line locations.
    external_symbolizer_path
    - Path to external symbolizer. If empty, the tool will search $PATH for the symbolizer.
    allow_addr2line
    - If set, allows online symbolizer to run addr2line binary to symbolize stack traces (addr2line will only be used if llvm-symbolizer binary is unavailable.
    strip_path_prefix
    - Strips this prefix from file paths in error reports.
    fast_unwind_on_check
    - If available, use the fast frame-pointer-based unwinder on internal CHECK failures.
    fast_unwind_on_fatal
    - If available, use the fast frame-pointer-based unwinder on fatal errors.
    fast_unwind_on_malloc
    - If available, use the fast frame-pointer-based unwinder on malloc/free.
    handle_ioctl
    - Intercept and handle ioctl requests.
    malloc_context_size
    - Max number of stack frames kept for each allocation/deallocation.
    log_path
    - Write logs to "log_path.pid". The special values are "stdout" and "stderr". The default is "stderr".
    log_exe_name
    - Mention name of executable when reporting error and append executable name to logs (as in "log_path.exe_name.pid").
    log_to_syslog
    - Write all sanitizer output to syslog in addition to other means of logging.
    verbosity
    - Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
    detect_leaks
    - Enable memory leak detection.
    leak_check_at_exit
    - Invoke leak checking in an atexit handler. Has no effect if detect_leaks=false, or if __lsan_do_leak_check() is called before the handler has a chance to run.
    allocator_may_return_null
    - If false, the allocator will crash instead of returning 0 on out-of-memory.
    print_summary
    - If false, disable printing error summaries in addition to error reports.
    print_module_map
    - OS X only (0 - don't print, 1 - print only once before process exits, 2 - print after each report).
    check_printf
    - Check printf arguments.
    handle_segv
    - Controls custom tool's SIGSEGV handler (0 - do not registers the handler, 1 - register the handler and allow user to set own, 2 - registers the handler and block user from changing it).
    handle_sigbus
    - Controls custom tool's SIGBUS handler (0 - do not registers the handler, 1 - register the handler and allow user to set own, 2 - registers the handler and block user from changing it).
    handle_abort
    - Controls custom tool's SIGABRT handler (0 - do not registers the handler, 1 - register the handler and allow user to set own, 2 - registers the handler and block user from changing it).
    handle_sigill
    - Controls custom tool's SIGILL handler (0 - do not registers the handler, 1 - register the handler and allow user to set own, 2 - registers the handler and block user from changing it).
    handle_sigfpe
    - Controls custom tool's SIGFPE handler (0 - do not registers the handler, 1 - register the handler and allow user to set own, 2 - registers the handler and block user from changing it).
    allow_user_segv_handler
    - Deprecated. True has no effect, use handle_sigbus=1. If false, handle_*=1 will be upgraded to handle_*=2.
    use_sigaltstack
    - If set, uses alternate stack for signal handling.
    detect_deadlocks
    - If set, deadlock detection is enabled.
    clear_shadow_mmap_threshold
    - Large shadow regions are zero-filled using mmap(NORESERVE) instead of memset(). This is the threshold size in bytes.
    color
    - Colorize reports: (always|never|auto).
    legacy_pthread_cond
    - Enables support for dynamic libraries linked with libpthread 2.2.5.
    intercept_tls_get_addr
    - Intercept __tls_get_addr.
    help
    - Print the flag descriptions.
    mmap_limit_mb
    - Limit the amount of mmap-ed memory (excluding shadow) in Mb; not a user-facing flag, used mosly for testing the tools
    hard_rss_limit_mb
    - Hard RSS limit in Mb. If non-zero, a background thread is spawned at startup which periodically reads RSS and aborts the process if the limit is reached
    soft_rss_limit_mb
    - Soft RSS limit in Mb. If non-zero, a background thread is spawned at startup which periodically reads RSS. If the limit is reached all subsequent malloc/new calls will fail or return NULL (depending on the value of allocator_may_return_null) until the RSS goes below the soft limit. This limit does not affect memory allocations other than malloc/new.
    heap_profile
    - Experimental heap profiler, asan-only
    allocator_release_to_os_interval_ms
    - Experimental. Only affects a 64-bit allocator. If set, tries to release unused memory to the OS, but not more often than this interval (in milliseconds). Negative values mean do not attempt to release memory to the OS.

    can_use_proc_maps_statm
    - If false, do not attempt to read /proc/maps/statm. Mostly useful for testing sanitizers.
    coverage
    - If set, coverage information will be dumped at program shutdown (if the coverage instrumentation was enabled at compile time).
    coverage_dir
    - Target directory for coverage dumps. Defaults to the current directory.
    full_address_space
    - Sanitize complete address space; by default kernel area on 32-bit platforms will not be sanitized
    print_suppressions
    - Print matched suppressions at exit.
    disable_coredump
    - Disable core dumping. By default, disable_coredump=1 on 64-bit to avoid dumping a 16T+ core file. Ignored on OSes that don't dump core by default and for sanitizers that don't reserve lots of virtual memory.
    use_madv_dontdump
    - If set, instructs kernel to not store the (huge) shadow in core file.
    symbolize_inline_frames
    - Print inlined frames in stacktraces. Defaults to true.
    symbolize_vs_style
    - Print file locations in Visual Studio style (e.g: file(10,42): ...
    dedup_token_length
    - If positive, after printing a stack trace also print a short string token based on this number of frames that will simplify deduplication of the reports. Example: 'DEDUP_TOKEN: foo-bar-main'. Default is 0.
    stack_trace_format
    - Format string used to render stack frames. See sanitizer_stacktrace_printer.h for the format description. Use DEFAULT to get default format.
    no_huge_pages_for_shadow
    - If true, the shadow is not allowed to use huge pages.
    strict_string_checks
    - If set check that string arguments are properly null-terminated
    intercept_strstr
    - If set, uses custom wrappers for strstr and strcasestr functions to find more errors.
    intercept_strspn
    - If set, uses custom wrappers for strspn and strcspn function to find more errors.
    intercept_strtok
    - If set, uses a custom wrapper for the strtok function to find more errors.
    intercept_strpbrk
    - If set, uses custom wrappers for strpbrk function to find more errors.
    intercept_strlen
    - If set, uses custom wrappers for strlen and strnlen functions to find more errors.
    intercept_strndup
    - If set, uses custom wrappers for strndup functions to find more errors.
    intercept_strchr
    - If set, uses custom wrappers for strchr, strchrnul, and strrchr functions to find more errors.
    intercept_memcmp
    - If set, uses custom wrappers for memcmp function to find more errors.
    strict_memcmp
    - If true, assume that memcmp(p1, p2, n) always reads n bytes before comparing p1 and p2.
    intercept_memmem
    - If set, uses a wrapper for memmem() to find more errors.
    intercept_intrin
    - If set, uses custom wrappers for memset/memcpy/memmove intrinsics to find more errors.
    intercept_stat
    - If set, uses custom wrappers for *stat functions to find more errors.
    intercept_send
    - If set, uses custom wrappers for send* functions to find more errors.
    decorate_proc_maps
    - If set, decorate sanitizer mappings in /proc/self/maps with user-readable names
    exitcode
    - Override the program exit status if the tool found an error
    abort_on_error
    - If set, the tool calls abort() instead of _exit() after printing the error report.
    suppress_equal_pcs
    - Deduplicate multiple reports for single source location in halt_on_error=false mode (asan only).
    print_cmdline
    - Print command line on crash (asan only).
    html_cov_report
    - Generate html coverage report.
    sancov_path
    - Sancov tool location.
    dump_instruction_bytes
    - If true, dump 16 bytes starting at the instruction that caused SEGV
    dump_registers
    - If true, dump values of CPU registers when SEGV happens. Only available on OS X for now.
    include
    - read more options from the given file
    include_if_exists
    - read more options from the given file (if it exists)
    =================================================================
    ==4911==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe84af3135 at pc 0x2ab12ca03533 bp 0x7ffe84af3000 sp 0x7ffe84af2790
    WRITE of size 7 at 0x7ffe84af3135 thread T0
    #0 0x2ab12ca03532 in __interceptor_vsprintf (/usr/lib64/libasan.so.5+0x55532)
    #1 0x2ab12ca038a6 in sprintf (/usr/lib64/libasan.so.5+0x558a6)
    #2 0x400de1 in test_oom_sprintf() /root/err/main2.cpp:22
    #3 0x40108d in main /root/err/main2.cpp:48
    #4 0x2ab12e1bdb34 in __libc_start_main (/usr/lib64/libc.so.6+0x21b34)
    #5 0x400c28 (/root/err/test_oom+0x400c28)

    Address 0x7ffe84af3135 is located in stack of thread T0 at offset 37 in frame
    #0 0x400ce1 in test_oom_sprintf() /root/err/main2.cpp:19

    This frame has 1 object(s):
    [32, 37) 'buf' <== Memory access at offset 37 overflows this variable
    HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
    (longjmp and C++ exceptions *are* supported)
    SUMMARY: AddressSanitizer: stack-buffer-overflow (/usr/lib64/libasan.so.5+0x55532) in __interceptor_vsprintf
    Shadow bytes around the buggy address:
    0x1000509565d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x1000509565e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x1000509565f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x100050956600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x100050956610: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x100050956620: 00 00 f1 f1 f1 f1[05]f2 f2 f2 f3 f3 f3 f3 00 00
    0x100050956630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x100050956640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x100050956650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x100050956660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0x100050956670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    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
    ==4911==ABORTING

遇到的问题和解决方法

  • 找不到 libasan_preinit.o
    安装和gcc版本匹配的libasan-devel库即可
  • 错误信息只能输出到屏幕,不能输出到日志
    一般是gcc版本<5.x导致,升级gcc和对应的libasan库即可
  • 升级到高版gcc后,程序执行后仍然不能输出错误信息到到日志文件中,或者设置的ASAN_OPTIONS参数根本没效果
    确保编译时添加了-fsanitize=address等编译参数后,仍无法输出,可以考虑在程序中添加环境变量信息输出验证是否设置了ASAN_OPTIONS
  • 是否可以在程序main函数入口处调用setenv函数来设置ASAN_OPTIONS来达到启用asan动态参数的目的
    经验证不可以,程序在执行main函数前libasan已经检测ASAN_OPTIONS参数,所以要在程序启动时传递或已经设置系统环境变量
  • 当前开发机gcc为4.x版本,怎么样可以快速的切换gcc到高版本且不破坏当前的开发环境
    安装SCL,通过SCL安装各个不同版本的gcc,且可以快速方便的切换不同版本, scl enable devtoolset-4 bash

    使用SCL 快速构建不同gcc/g++版本开发环境

    介绍

    SCL(Software Collections)可以让您能够在同一系统上构建,安装和使用多个版本的软件,而不会影响整个系统已安装的软件包。

devtoolset 就是按照 Software Collections 的规范打出来的一套 rpm 包。通过scl可以方便的切换不同版本的devtoolset开发环境。个人认为可以理解为类似docker的开发环境,只是更轻量,更方便。

SCL项目主页:https://www.softwarecollections.org/

安装及使用

安装scl源

1
yum install centos-release-scl scl-utils-build

列出scl源有哪些包可以用:

1
yum list all --enablerepo='centos-sclo-rh'

安装对应版本的devtoolset开发包

如:安装8.3版本的gcc、gcc-c++等开发环境需要的包执行如下安装命令

1
yum install -y devtoolset-8-gcc-gdb-plugin-8.3.1-3.1.el7.x86_64 devtoolset-8-libstdc++-devel-8.3.1-3.1.el7.x86_64 devtoolset-8-binutils-2.30-54.el7.x86_64 devtoolset-8-gcc-8.3.1-3.1.el7.x86_64 devtoolset-8-libasan-devel-8.3.1-3.1.el7.x86_64 devtoolset-8-gcc-c++-8.3.1-3.1.el7.x86_64 devtoolset-8-runtime-8.1-1.el7.x86_64

查看安装的devtoolset环境列表

1
scl -l

切换到devtoolset-8环境

1
scl enable devtoolset-8 bash

切换后效果如下:


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!