高效内存管理与调试技巧:深入解析 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;…

函数指针的用法

函數指標一些相關的用法和注意事項。以下將詳細說明&#xff1a; 1. 基本寫法: 這是最常見且明確的寫法&#xff0c;尤其在處理重載函數時非常有用。 void (GirlFriend::*girl2)(QString) &GirlFriend::hungry;void: 指標所指向的函數的回傳型別。(GirlFriend::*): 表示…

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…

如何让面向应急管理的多智能体具备自主决策能力

现代应急管理面对的是复杂、多变、跨部门的灾害与事故场景——从自然灾害&#xff08;洪水、地震、台风、火灾&#xff09;到公共安全事件&#xff08;疫情、重大安全事故&#xff09;&#xff0c;都需要在极短时间内进行快速响应和全局协同。传统的集中式指挥模式虽然有效&…

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…

用 HTML5 Canvas 和 JavaScript 实现流星雨特效

最近在研究前端动画效果时,实现了一个超酷的流星雨特效,今天来和大家分享下具体实现过程。 1,整体实现思路 这个流星雨特效主要由 HTML、CSS 和 JavaScript 协同完成。HTML 搭建基础结构,CSS 负责页面样式设计,JavaScript 实现星星和流星的动态效果。 效果展示: 用 HTM…

AI中的神经元与权重矩阵之间的关系;神经元连接角度看行和列的意义

AI中的神经元与权重矩阵之间的关系 目录 AI中的神经元与权重矩阵之间的关系神经元连接角度看行和列的意义AI中的神经元概念 在人工智能领域,特别是神经网络中,神经元是基本的计算单元,它是对生物神经元的一种抽象模拟。就像生物神经元接收来自其他神经元的电信号,经过处理后…

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.计算下…

欧几里得算法(简单理解版,非严格证明)

欧几里得算法用于求解两个整数的最大公约数&#xff0c;又称为辗转相除 依据的基本定理&#xff1a; GCD(a,b)GCD(a%b,b) 证明&#xff1a; 对于搞理论的人可能需要会严格证明&#xff0c;但是对于我们一般人而言&#xff0c;只要能理解其原理并记住即可&#xff0c;后者实际上…

插入式微型机顶盒来了

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

lamda表达式

提示&#xff1a;文章 文章目录 前言一、背景在使用lambda的时候&#xff0c;有几个参数是可以直接省略的&#xff1a; 二、题目问题探究 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; lamda表达式 一、背景 看c科二的时候有看到lamda表达式&#xff0c;就再次看了…

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

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