高效內存管理與調試技巧:深入解析 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.pswp.cn/web/65253.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65253.shtml
英文地址,請注明出處:http://en.pswp.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、【能力】服務…