高效内存管理与调试技巧:深入解析 AddressSanitizer

在现代 C++开发中,内存管理是一个至关重要但也容易出错的领域。即使使用了智能指针和其他高效工具,复杂的项目仍可能出现内存泄漏、非法访问等问题。为了解决这些问题,Google 开发了一个强大的工具——AddressSanitizer (ASan)。本文将详细介绍如何使用 ASan 高效调试内存问题,以及一些常见的最佳实践。

2. 什么是 AddressSanitizer?

AddressSanitizer 是一种快速内存错误检测工具,可以捕捉以下几类内存问题:

  1. 越界访问(Out-of-Bounds Access): 访问数组或容器之外的内存。例如:

    #include <iostream>int main() {int arr[5] = {0};arr[5] = 10; // 越界访问return 0;
    }
    
  2. 堆使用后释放(Use-After-Free): 访问已经被释放的堆内存。例如:

    #include <iostream>int main() {int* ptr = new int(10);delete ptr;*ptr = 20; // 使用已释放的内存return 0;
    }
    
  3. 堆内存泄漏(Memory Leaks): 未正确释放的堆内存。例如:

    #include <iostream>int main() {int* ptr = new int[10];// 未释放分配的内存return 0;
    }
    
  4. 栈缓冲区溢出(Stack Buffer Overflow): 非法访问栈上的内存。例如:

    #include <iostream>void recursive() {int arr[1000];recursive(); // 导致栈溢出
    }int main() {recursive();return 0;
    }
    
  5. 全局缓冲区越界(Global Buffer Overflow): 访问全局变量分配的内存之外的区域。例如:

    #include <iostream>char global_arr[10];int main() {global_arr[10] = 'A'; // 越界访问全局缓冲区return 0;
    }
    
  6. 返回后使用(Use-After-Return): 访问已退出函数的栈变量。

    #include <iostream>int* dangling_pointer() {int local_var = 42;return &local_var; // 返回局部变量的地址
    }int main() {int* ptr = dangling_pointer();std::cout << *ptr << std::endl; // 使用悬空指针return 0;
    }
    
  7. 作用域外使用(Use-After-Scope): 访问已超出作用域的变量。

    #include <iostream>
    #include <string>int main() {std::string* ptr;{std::string local_str = "hello";ptr = &local_str;} // local_str超出作用域std::cout << *ptr << std::endl; // 使用无效指针return 0;
    }
    
  8. 初始化顺序错误(Initialization Order Bugs): 在全局变量的构造函数中访问未初始化的变量。

    #include <iostream>struct A {A() { std::cout << b << std::endl; } // 访问未初始化的bstatic int b;
    };int A::b = 42;int main() {A a;return 0;
    }
    

2. 如何开启

编译器 flag

新近的编译机基本都支持 asan,下面是如何开启

  1. 在 GCC 或 Clang 中,启用 ASan 只需简单的编译选项: -fsanitize=address

CMake 设置

在使用 CMake 的项目中,可以通过以下配置启用 ASan:

  1. 全局设置

    add_compile_options(-fsanitize=address)
    add_link_options(-fsanitize=address)
    
  2. 也可以为单独的 target 设置

    target_compile_options(target -fsanitize=address)
    target_link_options(target -fsanitize=address)
    

5. AddressSanitizer 的错误报告

1. 错误输出

运行上述的越界访问的样例,程序会产生错误输出,内容如下

=================================================================
==58410==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x78be1a309034 at pc 0x599c6544a334 bp 0x7fffd3283890 sp 0x7fffd3283880
WRITE of size 4 at 0x78be1a309034 thread T0#0 0x599c6544a333 in main /home/aronic/playground/CSDNBlogSampleCode/address-sanitizer/out-of-bound.cpp:4#1 0x78be1c42a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#2 0x78be1c42a28a in __libc_start_main_impl ../csu/libc-start.c:360#3 0x599c6544a124 in _start (/home/aronic/playground/CSDNBlogSampleCode/build/out-of-bound+0x1124) (BuildId: 81ed0f02ffd8359b35cb7455896699d9e2b084bc)Address 0x78be1a309034 is located in stack of thread T0 at offset 52 in frame#0 0x599c6544a1f8 in main /home/aronic/playground/CSDNBlogSampleCode/address-sanitizer/out-of-bound.cpp:2This frame has 1 object(s):[32, 52) 'arr' (line 3) <== Memory access at offset 52 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/aronic/playground/CSDNBlogSampleCode/address-sanitizer/out-of-bound.cpp:4 in main
Shadow bytes around the buggy address:0x78be1a308d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a308e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a308e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a308f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a308f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x78be1a309000: f1 f1 f1 f1 00 00[04]f3 f3 f3 f3 f3 00 00 00 000x78be1a309080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a309100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a309180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a309200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x78be1a309280: 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:           00Partially addressable: 01 02 03 04 05 06 07Heap left redzone:       faFreed heap region:       fdStack left redzone:      f1Stack mid redzone:       f2Stack right redzone:     f3Stack after return:      f5Stack use after scope:   f8Global redzone:          f9Global init order:       f6Poisoned by user:        f7Container overflow:      fcArray cookie:            acIntra object redzone:    bbASan internal:           feLeft alloca redzone:     caRight alloca redzone:    cb
==58410==ABORTING

这个 ASan 输出详细地报告了程序中发生的**栈缓冲区溢出(stack-buffer-overflow)**错误,以下是解读每个关键部分的详细说明:


2. 错误概要

==58410==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x78be1a309034 at pc 0x599c6544a334 bp 0x7fffd3283890 sp 0x7fffd3283880
WRITE of size 4 at 0x78be1a309034 thread T0
  • 错误类型stack-buffer-overflow 表示在栈上的数组发生了越界访问。
  • 地址0x78be1a309034 是出错的内存地址。
  • 线程T0 表示发生错误的线程是主线程。
  • 操作类型WRITE of size 4,表明代码试图向越界地址写入 4 个字节的数据(可能是一个 int 类型)。

3. 错误发生的代码位置

#0 0x599c6544a333 in main /home/aronic/playground/CSDNBlogSampleCode/address-sanitizer/out-of-bound.cpp:4
  • 错误发生在文件 /home/aronic/playground/CSDNBlogSampleCode/address-sanitizer/out-of-bound.cpp 的第 4 行代码中。
  • 堆栈追踪(stack trace)显示了函数调用链中错误的位置:这里是 main 函数。

4. 详细地址信息

Address 0x78be1a309034 is located in stack of thread T0 at offset 52 in frame#0 0x599c6544a1f8 in main /home/aronic/playground/CSDNBlogSampleCode/address-sanitizer/out-of-bound.cpp:2
  • 地址:出错地址 0x78be1a309034 位于栈帧中,从栈帧的起始偏移量 52 开始。
  • 函数main 是栈帧所属的函数。

5. 变量信息

This frame has 1 object(s):[32, 52) 'arr' (line 3) <== Memory access at offset 52 overflows this variable
  • 变量arr 是一个栈上分配的数组,位于 [32, 52) 的地址范围。
  • 问题arr 的有效范围是 [32, 52),但访问发生在 52 偏移处,超出了变量的边界。

6. 提示信息

HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork(longjmp and C++ exceptions *are* supported)
  • 提示一些边界情况(如 swapcontextvfork)可能导致误报,但这里明显是栈溢出。

7. 总结

SUMMARY: AddressSanitizer: stack-buffer-overflow /home/aronic/playground/CSDNBlogSampleCode/address-sanitizer/out-of-bound.cpp:4 in main
  • 问题类型:stack-buffer-overflow
  • 错误位置:out-of-bound.cpp 的第 4 行。

8. Shadow Memory 显示

Shadow bytes around the buggy address:=>0x78be1a309000: f1 f1 f1 f1 00 00[04]f3 ...
  • f1:表示栈的左红区(Stack Left Redzone),即栈变量边界的保护区域。
  • f3:表示栈的右红区(Stack Right Redzone),越过这个区域会触发越界错误。
  • [04]:出错访问位置。

6. 如何配置 AddressSanitizer

ASan 提供了多种环境变量和运行时选项,以便更好地适应实际需求。以下是常见的配置选项:

6.1 环境变量

  1. ASAN_OPTIONS
    通过设置 ASAN_OPTIONS,可以自定义 ASan 的行为。以下是一些常用参数及其用途:
  • detect_leaks=1:启用内存泄漏检测(默认开启)。
  • halt_on_error=1:在检测到内存错误时立即停止程序运行。
  • verbosity=1:增加日志的详细程度,便于调试。
  • log_to_syslog=1:将错误日志写入系统日志,而非标准输出。
  • allocator_may_return_null=1:当内存分配失败时返回 NULL 而非终止程序。
  • malloc_context_size=10:设置堆栈跟踪的深度,默认值为 10。
  • strict_string_checks=1:启用更严格的字符串操作检查。

这些参数可以灵活调整,以适应不同的调试需求。

示例:

export ASAN_OPTIONS=detect_leaks=1:halt_on_error=1
  • detect_leaks=1 启用内存泄漏检测(默认开启)。
  • halt_on_error=1 检测到错误时立即停止程序。
  1. LSAN_OPTIONS
    如果要单独控制内存泄漏检测,可设置 LSAN_OPTIONS

示例:

export LSAN_OPTIONS=suppressions=leak_ignore.txt

6.2 报告压缩

为减少报告的冗长,可以启用报告压缩:

export ASAN_OPTIONS=log_to_syslog=1:verbosity=1

6.3 抑制特定错误

如果某些错误可以忽略,可以通过抑制文件指定。

示例抑制文件 suppressions.txt

leak:example_function
heap-buffer-overflow:another_function

运行时使用:

export ASAN_OPTIONS=suppressions=suppressions.txt

9. 内部原理

AddressSanitizer 的工作原理核心在于影子内存(Shadow Memory)和红黑树(Red-Black Tree)的使用,这些技术帮助高效检测内存问题。

  1. 影子内存(Shadow Memory)

    • 影子内存是程序实际内存的紧凑映射,每个影子字节表示实际内存中的 8 字节状态。
    • 地址映射公式:
      ShadowAddr = (MemAddr >> 3) + Offset
      
      其中 Offset 是一个固定值,确保影子内存区域与实际内存隔离。
    • 影子字节的值用于标记实际内存是否可访问。例如:
      • 0: 完全可访问。
      • 非零值:部分或完全不可访问。
  2. 插桩代码检测

    • 编译器在编译时插入检查代码,每次内存分配、释放或访问都会检查影子内存。
    • 如果检测到非法访问(如越界、使用已释放内存),ASan 会生成详细的错误报告。
  3. 红黑树存储元信息

    • ASan 使用红黑树记录分配的内存块信息,包括大小和位置。
    • 访问内存时,通过红黑树快速验证操作是否合法。

这种结合影子内存映射和红黑树的机制,使得 ASan 在运行时能快速、准确地捕捉内存问题,性能开销显著低于传统工具如 Valgrind,同时提供详细的上下文信息,方便开发者定位和修复问题。

8. AddressSanitizer 的最佳实践

  1. 开发早期启用 ASan
    在开发初期就启用 ASan,可以及时发现潜在问题,避免问题堆积。这是因为早期发现问题不仅可以减少后期修复的复杂度,还能显著降低技术债务的累积。此外,ASan 的错误报告详细而直观,便于快速定位和解决问题。

  2. 结合其他工具使用
    将 ASan 与静态分析工具(如 Clang-Tidy)结合,全面提升代码质量。

  3. 定期运行回归测试
    在 CI/CD 管道中集成 ASan,确保代码改动不会引入新的内存问题。

  4. 注意性能开销
    ASan 可能导致运行速度降低,建议仅在调试环境中启用。

9. 总结

AddressSanitizer 是一个高效的内存问题检测工具,特别适合现代 C++开发中的调试需求。它通过影子内存(Shadow Memory)和红黑树记录分配信息,快速检测和报告内存错误。ASan 的高效机制能显著提升代码的健壮性和性能,是开发复杂内存操作项目的重要工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/65253.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 内置组件之Teleport

文章目录 Vue3 内置组件之Teleport概述用法 Vue3 内置组件之Teleport 概述 Teleport 中文翻译为“瞬间移动”&#xff0c;顾名思义&#xff0c;在Vue3 中 <Teleport> 组件可以将组件中内容移动到指定的目标元素上。 用法 <script setup> import {ref} from &qu…

【我的 PWN 学习手札】IO_FILE 之 FSOP

FSOP&#xff1a;File Stream Oriented Programming 通过劫持 _IO_list_all 指向伪造的 _IO_FILE_plus&#xff0c;进而调用fake IO_FILE 结构体对象中被伪造的vtable指向的恶意函数。 目录 前言 一、glibc-exit函数浅析 二、FSOP 三、Largebin attack FSOP &#xff08;…

DDcGAN_多分辨率图像融合的双鉴别条件生成对抗网络_y译文马佳义

摘要&#xff1a; 在本文中&#xff0c;我们提出了一种新的端到端模型&#xff0c;称为双鉴别条件生成对抗网络&#xff08;DDcGAN&#xff09;&#xff0c;用于融合不同分辨率的红外和可见光图像。我们的方法建立了一个生成器和两个鉴别器之间的对抗博弈。生成器的目的是基于特…

springboot配置线程池

直接上代码 配置 定义一个配置类 创建一个springboot能扫描到的地方创建一个线程池配置类 配置信息 package com.example.demonew.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import or…

【君正T31开发记录】12.编译工具相关总结及介绍

移植交叉工具包的时候&#xff0c;发现这是很多工具的集合包&#xff1b;以及写makefile的时候&#xff0c;也需要了解下这些工具的作用及用法&#xff0c;这里总结记录一下常见的工具及相关用法。 g C编译器&#xff0c;用于编译C源代码文件&#xff0c;这个很常见&#xff0…

List-顺序表--2

目录 1、ArrayList 2、ArrayList构造方法 3、ArrayList常见方法 4、ArrayList的遍历 5、ArrayList的扩容机制 6、ArrayList的具体使用 6.1、杨辉三角 6.2、简单的洗牌算法 1、ArrayList 在集合框架中&#xff0c;ArrayList 是一个普通的类&#xff0c;实现了 List 接口…

lambda用法及其原理

目录 lambda形式lambda用法1.sort降序2.swap3.捕捉列表 习题解题 lambda形式 [capture-list](parameters)->return type{function boby}[capture-list]&#xff1a;[捕捉列表]用于捕捉函数外的参数&#xff0c;可以为空&#xff0c;但不能省略&#xff1b;(parameters) &am…

基于ASP.NET的动漫网站

一、系统架构与技术实现 系统架构&#xff1a;基于ASP.NET的MVC框架构建&#xff0c;实现网站的层次结构&#xff0c;使得网站更加易于维护和扩展。 技术实现&#xff1a;利用ASP.NET的技术特点&#xff0c;如强大的后端开发能力、丰富的UI控件等&#xff0c;结合前端技术如HT…

Visual studio code编写简单记事本exe笔记

安装扩展cmake tools c/c c/c Extension pack CMakeLists.txt cmake_minimum_required(VERSION 3.20) project(NotepadApp)set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)# Windows specific settings if(WIN32)set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)s…

Linux 35.6 + JetPack v5.1.4之编译 pytorch升级

Linux 35.6 JetPack v5.1.4之编译 pytorch升级 1. 源由2. 升级步骤1&#xff1a;获取二进制版本步骤2&#xff1a;安装二进制版本步骤3&#xff1a;获取torchvision步骤4&#xff1a;安装torchvision步骤5&#xff1a;检查安装版本 3. 使用4. 补充4.1 torchvision版本问题4.2 …

计算机网络--根据IP地址和路由表计算下一跳

一、必备知识 1.无分类地址IPV4地址网络前缀主机号 2.每个IPV4地址由32位二进制数组成 3. /15这个地址表示网络前缀有15位&#xff0c;那么主机号32-1517位。 4.地址掩码&#xff08;子网掩码&#xff09;&#xff1a;所对应的网络前缀为1&#xff0c;主机号为0。 5.计算下…

插入式微型机顶盒来了

快科技1月6日消息&#xff0c;据国家广播电视总局今日消息&#xff0c;国家广播电视总局为首款以插入式微型机顶盒品类通过入网检测的设备颁发了入网认定证书。 这是插入式微型机顶盒批量部署进程中的又一大进展。同时&#xff0c;广播电视科学研究院依据行业标准建成了插入式…

XXL-RPC v1.8.1 | RPC服务框架

Release Notes 1、【安全】序列化安全性增强&#xff0c;默认开启package安全空间机制&#xff1b;2、【扩展】序列化扩展性增强&#xff0c;支持自定义序列化package白名单&#xff1b;3、【优化】序列化类型主动检测&#xff0c;提升问题定位效率&#xff1b;4、【能力】服务…

前端路由layout布局处理以及菜单交互(三)

上篇介绍了前端项目部署以及基本依赖的应用&#xff0c;这次主要对于路由以及布局进行模块化处理 一、 创建layout模块 1、新建src/layout/index.vue <template><el-container class"common-layout"><!-- <el-aside class"aside">&l…

Spring Boot(4)使用 IDEA 搭建 Spring Boot+MyBatis 项目全流程实战

文章目录 一、⚡搞个引言二、⚡开始搭建 Spring Boot 项目吧&#xff01;2.1 启动 IDEA 并创建新项目2.2 选择项目依赖2.3 完成项目创建 三、&#x1f4d8;项目结构剖析四、✍配置数据库连接五、✍ 创建 MyBatis 相关组件5.1 实体类&#xff08;Entity&#xff09;5.2 Mapper 接…

【数据可视化-11】全国大学数据可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

141.《mac m1安装mongodb详细教程》

文章目录 下载从官网下载安装包 下载后双击解压出文件夹安装文件名修改为 mongodb配置data存放位置和日志log的存放位置启动方式一方式二方式二:输入mongo报错以及解决办法 本人电脑 m2 pro,属于 arm 架构 下载 官网地址: mongodb官网 怎么查看自己电脑应该下载哪个版本,输入…

frameworks 之 Winscope 工具

frameworks 之 Winscope 工具 1. 手机端开启2. 加载追踪的文件2.1 Android12 3. 分析文件 Winscope 是一款 Web 工具&#xff0c;可以让用户在动画和转换期间和之后记录、重放和分析多个系统服务的状态。Winscope 将所有相关的系统服务状态记录在一个跟踪文件中。使用带有跟踪文…

【姿态估计实战】使用OpenCV和Mediapipe构建锻炼跟踪器【附完整源码与详细说明】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…