日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Linux中的常用內(nèi)存問題檢測工具

 老匹夫 2015-07-13

原文地址:http://blog.csdn.net/jinzhuojun/article/details/46659155

C/C++等底層語言在提供強(qiáng)大功能及性能的同時,其靈活的內(nèi)存訪問也帶來了各種糾結(jié)的問題。如果crash的地方正是內(nèi)存使用錯誤的地方,說明你人品好。如果crash的地方內(nèi)存明顯不是consistent的,或者內(nèi)存管理信息都已被破壞,并且還是隨機(jī)出現(xiàn)的,那就比較麻煩了。當(dāng)然,祼看code打log是一個辦法,但其效率不是太高,尤其是在運(yùn)行成本高或重現(xiàn)概率低的情況下。另外,靜態(tài)檢查也是一類方法,有很多工具(lint, cppcheck, klockwork, splint, o, etc.)。但缺點是誤報很多,不適合針對性問題。另外好點的一般還要錢。最后,就是動態(tài)檢查工具。下面介紹幾個Linux平臺下主要的運(yùn)行時內(nèi)存檢查工具。絕大多數(shù)都是開源免費(fèi)且支持x86和ARM平臺的。

首先,比較常見的內(nèi)存問題有下面幾種:
· memory overrun:寫內(nèi)存越界
· double free:同一塊內(nèi)存釋放兩次
· use after free:內(nèi)存釋放后使用
· wild free:釋放內(nèi)存的參數(shù)為非法值
· access uninitialized memory:訪問未初始化內(nèi)存
· read invalid memory:讀取非法內(nèi)存,本質(zhì)上也屬于內(nèi)存越界
· memory leak:內(nèi)存泄露
· use after return:caller訪問一個指針,該指針指向callee的棧內(nèi)內(nèi)存
· stack overflow:棧溢出

針對上面的問題,主要有以下幾種方法:
1. 為了檢測內(nèi)存非法使用,需要hook內(nèi)存分配和操作函數(shù)。hook的方法可以是用C-preprocessor,也可以是在鏈接庫中直接定義(因為Glibc中的malloc/free等函數(shù)都是weak symbol),或是用LD_PRELOAD。另外,通過hook strcpy(),memmove()等函數(shù)可以檢測它們是否引起buffer overflow。
2. 為了檢查內(nèi)存的非法訪問,需要對程序的內(nèi)存進(jìn)行bookkeeping,然后截獲每次訪存操作并檢測是否合法。bookkeeping的方法大同小異,主要思想是用shadow memory來驗證某塊內(nèi)存的合法性。至于instrumentation的方法各種各樣。有run-time的,比如通過把程序運(yùn)行在虛擬機(jī)中或是通過binary translator來運(yùn)行;或是compile-time的,在編譯時就在訪存指令時就加入檢查操作。另外也可以通過在分配內(nèi)存前后加設(shè)為不可訪問的guard page,這樣可以利用硬件(MMU)來觸發(fā)SIGSEGV,從而提高速度。
3. 為了檢測棧的問題,一般在stack上設(shè)置canary,即在函數(shù)調(diào)用時在棧上寫magic number或是隨機(jī)值,然后在函數(shù)返回時檢查是否被改寫。另外可以通過mprotect()在stack的頂端設(shè)置guard page,這樣棧溢出會導(dǎo)致SIGSEGV而不至于破壞數(shù)據(jù)。

以上方法有些強(qiáng)于功能,有些勝在性能,有些則十分方便易用,總之各有千秋。以下是幾種常用工具在Linux x86_64平臺的實驗結(jié)果,注意其它平臺可能結(jié)果有差異。另外也可能由于版本過老,編譯環(huán)境差異,姿勢不對,總之各種原因造成遺漏,如有請諒解~

Tool\Problem memory overrun double free use after free wild free access uninited read invalid memory memory leak use after return stack overflow
Memory checking tools in Glibc Yes Yes Yes Yes(if use memcpy, strcpy, etc)
TCMalloc(Gperftools) Yes
Valgrind Yes Yes Yes Yes Yes Yes Yes Yes Yes
Address Sanitizer(ASan) Yes Yes Yes Yes (Memory Sanitizer) Yes Yes Yes Yes
Memwatch Yes Yes Yes
Dr.Memory Yes Yes Yes Yes Yes Yes Yes Yes
Electric Fence Yes Yes Yes Yes
Dmalloc Yes Yes Yes Yes Yes

下面簡單介紹一下這些工具以及基本用法。更詳細(xì)用法請參見各自manual。

Memory checking tools in Glibc

Glibc中自帶了一些Heap consistency checking機(jī)制。

MALLOC_CHECK_

用mallopt()的M_CHECK_ACTION可以設(shè)置內(nèi)存檢測行為,設(shè)MALLOC_CHECK_環(huán)境變量效果也是一樣的。從Glibc 2.3.4開始,默認(rèn)為3。即打印出錯信息,stack trace和memory mapping,再退出程序。設(shè)置LIBC_FATAL_STDERR_=1可以將這些信息輸出到stderr。比如運(yùn)行以下有double free的程序:
$ MALLOC_CHECK_=3 ./bug
會打印如下信息然后退出:

*** Error in `./bug': free(): invalid pointer: 0x00000000010d6010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7338f)[0x7f367073238f]
/lib/x86_64-linux-gnu/libc.so.6(+0x81fb6)[0x7f3670740fb6]
./bug[0x400845]
./bug[0x400c36]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f36706e0ec5]
./bug[0x400729]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:01 2893041                            /home/jzj/code/bug
00601000-00602000 r--p 00001000 08:01 2893041                            /home/jzj/code/bug
00602000-00603000 rw-p 00002000 08:01 2893041                            /home/jzj/code/bug
010d6000-010f7000 rw-p 00000000 00:00 0                                  [heap]
7f36704a8000-7f36704be000 r-xp 00000000 08:01 4203676                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f36704be000-7f36706bd000 ---p 00016000 08:01 4203676                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f36706bd000-7f36706be000 r--p 00015000 08:01 4203676                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f36706be000-7f36706bf000 rw-p 00016000 08:01 4203676                    /lib/x86_64-linux-gnu/libgcc_s.so.1
…
Aborted (core dumped)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

mcheck

mcheck是Glibc中的堆內(nèi)存一致性檢查機(jī)制。使用時只要加上頭文件:

#include <mcheck>
  • 1

再在要開始檢查的地方加上:

if (mcheck(NULL) != 0) {
    fprintf(stderr, "mcheck() failed\n");

    exit(EXIT_FAILURE);
}
…
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

編譯時加-lmcheck然后運(yùn)行即可:
$ g++ -Wall -g problem.cpp -o bug -lmcheck

_FORTIFY_SOURCE

宏_FORTIFY_SOURCE提供輕量級的buffer overflow檢測。設(shè)置后會調(diào)用Glibc里帶_chk后綴的函數(shù),做一些運(yùn)行時檢查。主要檢查各種字符串緩沖區(qū)溢出和內(nèi)存操作。比如memmove, memcpy, memset, strcpy, strcat, vsprintf等。注意一些平臺上編譯時要加-O1或以上優(yōu)化。這樣就可以檢查出因為那些內(nèi)存操作函數(shù)導(dǎo)致的緩沖溢出問題:
$ g++ -Wall -g -O2 -D_FORTIFY_SOURCE=2 problem.cpp -o bug

*** buffer overflow detected ***: ./bug terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7338f)[0x7f9976e1638f]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x5c)[0x7f9976eadc9c]
/lib/x86_64-linux-gnu/libc.so.6(+0x109b60)[0x7f9976eacb60]
  • 1
  • 2
  • 3
  • 4
  • 5

mtrace

mtrace可以用于檢查malloc/free是否正確配對。用時用mtrace()和muntrace()表示開始和結(jié)束內(nèi)存分配trace(如果檢測到結(jié)束結(jié)尾的話可以不用muntrace())。但這是簡單地記錄沒有free對應(yīng)的malloc,可能會有一些false alarm。

#include <mcheck.h>
mtrace(); 
// …
muntrace(); 
  • 1
  • 2
  • 3
  • 4

然后編譯:
$ g++ -Wall -g problem.cpp -o bug
運(yùn)行時先設(shè)輸出的log文件:
$ export MALLOC_TRACE=output.log
用mtrace命令將輸出文件變得可讀:
$ mtrace ./bug $MALLOC_TRACE
就可以得到哪些地方的內(nèi)存申請還沒有被free掉。

Memory not freed:
           Address     Size     Caller
0x00000000008d4520    0x400  at /home/jzj/code/problem.cpp:73
  • 1
  • 2
  • 3

Gperftools

Gperftools(Google Performance Tools)為一組工具集,包括了thread-caching malloc(TCMalloc)和CPU profiler等組件。TCMalloc和Glibc中的ptmalloc相比更快,并可以有效減少多線程之間的競爭,因為它會為每個線程單獨(dú)分配線程本地的Cache。這里先只關(guān)注它的內(nèi)存相關(guān)組件。通過tcmalloc可以做heap-checking和heap-profiling。

如果懶得build,Ubuntu可以如下安裝:
$ sudo apt-get install libgoogle-perftool-dev google-perftools
然后編譯時加-ltcmalloc,注意一定要放最后鏈接,如:
$ g++ -Wall -g problem.cpp -g -o bug -ltcmalloc
編譯時不鏈接的話就也可以用LD_PRELOAD:
$ export LD_PRELOAD=”/usr/lib/libtcmalloc.so”
運(yùn)行的時候執(zhí)行:
$ HEAPCHECK=normal ./bug
就可以報出內(nèi)存泄露:

Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 1024 bytes in 1 objects
The 1 largest leaks:
*** WARNING: Cannot convert addresses to symbols in output below.
*** Reason: Cannot find 'pprof' (is PPROF_PATH set correctly?)
*** If you cannot fix this, try running pprof directly.
Leak of 1024 bytes in 1 objects allocated from:
    @ 400ba3 
    @ 400de0 
    @ 7fe1be24bec5 
    @ 400899 
    @ 0 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

如果想只檢查某部分,可以用HeapProfileLeakChecker生成內(nèi)存快照,然后執(zhí)行完要檢查部分后調(diào)用assert(checker.NoLeaks())。具體用法見:http://goog-perftools./doc/heap_checker.html

更詳細(xì)的信息可以用google-pprof獲得,如:

$ google-pprof ./bug "/tmp/bug.1353._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv
  • 1

關(guān)于Tcmalloc更多的配置信息可以參見:http://gperftools./svn/trunk/doc/tcmalloc.html

Valgrind

Valgrind是Valgrind core和Valgrind工具插件的集合,除了用于檢查內(nèi)存錯誤,還可以用來分析函數(shù)調(diào)用,緩存使用,多線程競爭,堆棧使用等問題。這里只關(guān)注memcheck工具,因為太常用 ,它默認(rèn)就是打開的。其原理是讓程序跑在一個虛擬機(jī)上,因此速度會慢幾十倍。好在現(xiàn)實中很多程序是IO bound的,所以很多時候沒有慢到忍無可忍的地步。好處是它不需要重新編譯目標(biāo)程序。它會通過hash表記錄每個heap block,同時通過shadow memory記錄這些內(nèi)存區(qū)域的信息。這樣就可以在每次訪存時檢查其合法性。

運(yùn)行時可根據(jù)需要加配置參數(shù),如:

$ valgrind --tool=memcheck --error-limit=no --track-origins=yes  --trace-children=yes --track-fds=yes ./bug 
  • 1

如memory overrun就會報以下錯誤:

==1735== Invalid write of size 1
==1735==    at 0x4008A7: overrun() (problem.cpp:26)
==1735==    by 0x400C2B: main (problem.cpp:127)
==1735==  Address 0x51fc460 is not stack'd, malloc'd or (recently) free'd
==1735== 
  • 1
  • 2
  • 3
  • 4
  • 5

use after free檢測結(jié)果:

==1739== Invalid write of size 1
==1739==    at 0x4C2E51C: __GI_strncpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1739==    by 0x40098B: use_after_free() (problem.cpp:46)
==1739==    by 0x400C3F: main (problem.cpp:133)
==1739==  Address 0x51fc040 is 0 bytes inside a block of size 1,024 free'd
==1739==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1739==    by 0x400975: use_after_free() (problem.cpp:45)
==1739==    by 0x400C3F: main (problem.cpp:133)
==1739== 
==1739== Invalid write of size 1
==1739==    at 0x4C2E5AC: __GI_strncpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1739==    by 0x40098B: use_after_free() (problem.cpp:46)
==1739==    by 0x400C3F: main (problem.cpp:133)
==1739==  Address 0x51fc045 is 5 bytes inside a block of size 1,024 free'd
==1739==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1739==    by 0x400975: use_after_free() (problem.cpp:45)
==1739==    by 0x400C3F: main (problem.cpp:133)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

access uninitialized memory結(jié)果:

==1742== Conditional jump or move depends on uninitialised value(s)
==1742==    at 0x4EB17F1: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:867)
==1742==    by 0x4E819CF: vfprintf (vfprintf.c:1661)
==1742==    by 0x4E8B498: printf (printf.c:33)
==1742==    by 0x400AA6: access_uninit() (problem.cpp:72)
==1742==    by 0x400C5A: main (problem.cpp:142)
==1742==  Uninitialised value was created by a heap allocation
==1742==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1742==    by 0x400A87: access_uninit() (problem.cpp:71)
==1742==    by 0x400C5A: main (problem.cpp:142)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

像memory overrun和use after free這類問題比較難搞是因為出錯的時候往往不是第一現(xiàn)場。用Valgrind就比較容易抓到第一現(xiàn)場。如果一個對象A被釋放后,同一塊內(nèi)存再次被申請為對象B,但程序中還是通過指向?qū)ο驛的dangling pointer進(jìn)行訪問,會覆蓋已有數(shù)據(jù)或者讀出錯誤數(shù)據(jù)。但這種情況Valgrind檢查不出來,因為Valgrind不會做語義上的分析。但是Valgrind可以配置內(nèi)存分配策略,通過設(shè)置空閑內(nèi)存隊列大小和優(yōu)先級讓被釋放的內(nèi)存不馬上被重用。從而增大抓到此類問題的概率。

對于棧中內(nèi)存,Memcheck只會做未初始化數(shù)據(jù)訪問的檢測,而不會做?;蛉?jǐn)?shù)組中的越界檢測。這是由SGCheck來完成的,它與memcheck功能互補(bǔ)。使用SGCheck只需在valgrind后加上–tool=exp-sgcheck參數(shù)即可。

另外memcheck還提供一系列參數(shù)可以調(diào)整檢測策略,具體可參見Valgrind User Manual或者http:///docs/manual/mc-manual.html

Address sanitizer (ASan)

早先是LLVM中的特性,后被加入GCC 4.8。在GCC 4.9后加入對ARM平臺的支持。因此用時不需要第三方庫,通過在編譯時指定flag即可打開開關(guān)。它是 Mudflap的替代品(Mudflap從GCC 4.9開始不再支持,指定了也不做事)。ASan在編譯時在訪存操作中插入額外指令,同時通過Shadow memory來記錄和檢測內(nèi)存的有效性。slowdown官方稱為2x左右。

使用時只要在CFLAGS中加上如下flag。注意如果鏈接so,只有可執(zhí)行文件需要加flag。
$ g++ -Wall -g problem.cpp -o bug -fsanitize=address -fno-omit-frame-pointer
直接運(yùn)行,檢測出錯誤時會報出類似以下錯誤:

==22543==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61900000fea0 at pc 0x400f22 bp 0x7ffe3c21be90 sp 0x7ffe3c21be88
WRITE of size 1 at 0x61900000fea0 thread T0
    #0 0x400f21 in overrun() /home/jzj/code/problem.cpp:26
    #1 0x401731 in main /home/jzj/code/problem.cpp:127
    #2 0x7fb2a46b8ec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #3 0x400d08 (/home/jzj/code/bug+0x400d08)

==26753==ERROR: AddressSanitizer: attempting double-free on 0x61900000fa80 in thread T0:
    #0 0x7f591b4ba5c7 in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.1+0x545c7)
    #1 0x400e46 in double_free() /home/jzj/code/problem.cpp:17
    #2 0x40173b in main /home/jzj/code/problem.cpp:130
    #3 0x7f591b0c2ec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #4 0x400d08 (/home/jzj/code/bug+0x400d08)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

檢測一些特定問題需要加上專門的選項,比如要檢查訪問指向已被釋放的??臻g需要加上:
ASAN_OPTIONS=detect_stack_use_after_return=1
如果要檢測memory leak需要加上:
ASAN_OPTIONS=detect_leaks=1

各種參數(shù)配置請參見:https://code.google.com/p/address-sanitizer/wiki/Flags

Address-sanitizer是Sanitizer系工具中的一員。有一部分功能是在其余工具里,比如memory leak檢測在LeakSanitizer中,uninitialized memory read檢測在MemorySanitizer中。data race檢測在ThreadSanitizer中。它們最初都是LLVM中的特性,后被移植到GCC,所以用GCC的話最好用4.9,至少也是4.8以后版本。

AddressSanitizer不能檢測讀未初始化內(nèi)存,而這MemorySanitizer(MSan)能做到。它包含compiler instrumentation模塊和run-time的庫。目前只支持Linux x86_64平臺。使用時需在編譯選項加-fsanitize=memory -fPIE -pie,為了得到更詳細(xì)的信息,最好加上-fno-omit-frame-pointer和-fsanitize-memory-track-origins。它實現(xiàn)了Valgrind的部分功能,但由于使用了compile-time instrumentation,所以速度更快。可惜目前只在LLVM上有,在GCC上還沒有,暫且略過。

Memwatch

Memwatch是一個輕量級的內(nèi)存問題檢測工具。主要用于檢測內(nèi)存分配釋放相關(guān)問題及內(nèi)存越界訪問問題。通過C preprocessor,Memwatch替換所有 ANSI C的內(nèi)存分配 函數(shù),從而記錄分配行為。注意它不保證是線程安全的。效率上,大塊分配不受影響,小塊分配會受影響,因此它沒法使用原分配函數(shù)中的memory pool。最壞情況下會有3-5x的slowdown。它可以比較方便地模擬內(nèi)存受限情況。對于未初始化內(nèi)存訪問,和已釋放內(nèi)存訪問,Memwatch會poison相應(yīng)內(nèi)存(分配出來寫0xFE,釋放內(nèi)存寫0xFD)從而在出錯時方便調(diào)試。

使用時需要修改源碼。該庫需要單獨(dú)下載:
http://www./sourcecode/memwatch/
然后在要檢查的代碼中包含頭文件:

#include "memwatch.h"
  • 1

然后加下面宏編譯:
$ gcc -DMEMWATCH -DMW_STDIO test.c memwatch.c -o test
默認(rèn)結(jié)果輸出在memwatch.log。比如程序如果有double free的話會輸出:

Modes: __STDC__ 64-bit mwDWORD==(unsigned int)
mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56
double-free: <3> test.c(17), 0x25745e0 was freed from test.c(16)
Stopped at Sun Jun 14 10:57:15 2015

Memory usage statistics (global):
 N)umber of allocations made: 1
 L)argest memory usage      : 1024
 T)otal of all alloc() calls: 1024
 U)nfreed bytes totals      : 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Memory leak的輸出:

Modes: __STDC__ 64-bit mwDWORD==(unsigned int)
mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56
Stopped at Sun Jun 14 10:56:22 2015
unfreed: <1> test.c(63), 1024 bytes at 0x195f5e0    {FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE ................}

Memory usage statistics (global):
 N)umber of allocations made: 1
 L)argest memory usage      : 1024
 T)otal of all alloc() calls: 1024
 U)nfreed bytes totals      : 1024
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Electric Fence

Electric Fence主要用于追蹤buffer overflow的讀和寫。它利用硬件來抓住越界訪問的指令。其原理是為每一次內(nèi)存申請額外申請一個page或一組page,然后把這些buffer范圍外的page設(shè)為不可讀寫。這樣,如果程序訪問這些區(qū)域,由于頁表中這個額外page的權(quán)限是不可讀寫,會產(chǎn)生段錯誤。那些被free()釋放的內(nèi)存也會被設(shè)為不可訪問,因此訪問也會產(chǎn)生段錯誤。因為讀寫權(quán)限以頁為單位,所以如果多的頁放在申請內(nèi)存區(qū)域后,可防止overflow。如果要防止underflow,就得用環(huán)境變量EF_PROTECT_BELOW在區(qū)域前加保護(hù)頁。因為Electric Fence至少需要丙個頁來滿足內(nèi)存分配申請,因此內(nèi)存使用會非常大,好處是它利用了硬件來捕獲非法訪問,因此速度快。也算是空間換時間吧。

目前支持Window, Linux平臺,語言支持C/C++。限制包括無法檢測使用未初始化內(nèi)存,memory leak等。同時它不是線程安全的。Ubuntu上懶得編譯可以安裝現(xiàn)成的:
$ sudo apt-get install electric-fence

它是以庫的方式需要被鏈接到程序中:
$ g++ -Wall -g problem.cpp -o bug -lefence
或者用LD_PRELOAD,不過記得不要同時鏈接其它的malloc debugger庫。
$ export LD_PRELOAD=libefence.so.0.0
另外,EF_PROTECT_BELOW,EF_PROTECT_FREE,EF_ALLOW_MALLOC_0和EF_FILL這些環(huán)境變量都是用來控制其行為的??梢詤⒁妋anual:http://linux./man/3/efence

比如memory overrun和double free就可以得到如下結(jié)果:

  Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Segmentation fault (core dumped)

  Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>

ElectricFence Aborting: free(7fc1c17c8c00): address not from malloc().
Illegal instruction (core dumped)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

它無法在log中打出詳細(xì)信息,但如果運(yùn)行前打開了coredump:
$ ulimit -c unlimited
就可以gdb打開coredump來分析了:
$ gdb ./bug -c core

注意因為多數(shù)平臺在分配時遇到block size不是word size整數(shù)倍時會通過加padding byte進(jìn)行word alignment。如果是在padded area中出現(xiàn)overrun則無法檢測。這里可以通過在程序中設(shè)置EN_ALIGNMENT=1來防止byte padding,從而更容易檢測off by one的問題。

DUMA(http://duma./)從Electric Fence中fork出來并加入一些其它特性,比如leak detection,Windows支持等。

Dmalloc

比較經(jīng)典的內(nèi)存檢測工具,雖然N年沒更新了。dmalloc通過在分配區(qū)域增加padding magic number的做法來檢測非法訪問,因此它能夠檢測到問題但不能檢測出哪條指令出的錯。Dmalloc只能檢測越界寫,但不能檢測越界讀。另外,Dmalloc只檢測堆上用malloc系函數(shù)(而不是sbrk()或mmap())分配的內(nèi)存,而無法對棧內(nèi)存和靜態(tài)內(nèi)存進(jìn)行檢測。 本質(zhì)上它也是通過hook malloc(), realloc(), calloc(),free()等內(nèi)存管理函數(shù),還有strcat(), strcpy()等內(nèi)存操作函數(shù),來檢測內(nèi)存問題。它支持x86, ARM平臺,語言上支持C/C++,并且支持多線程。

使用時可以先從官網(wǎng)下載源碼包(http:///releases/),然后編譯安裝:
$ tar zxvf dmalloc-5.5.2.tgz
$ cd dmalloc-5.5.2
$ ./configure
$ make && make install

少量修改源代碼。只需要加上下面的頭文件:

#ifdef DMALLOC
#include "dmalloc.h"
#endif
  • 1
  • 2
  • 3

然后編譯時CFLAGS加上 -DDMALLOC -DDMALLOC_FUNC_CHECK,如:
$ g++ -Wall -g -DDMALLOC -DDMALLOC_FUNC_CHECK problem.cpp -o bug -ldmalloc

dmalloc的配置選項可以通過設(shè)置環(huán)境變量DMALLOC_OPTIONS來實現(xiàn),例如:
$ export DMALLOC_OPTIONS=log=logfile,check-fence,check-blank,check-shutdown,check-heap,check-funcs,log-stats,log-non-free,print-messages,log-nonfree-space
這些用法可參見:
http:///docs/latest/online/dmalloc_26.html
http:///docs/latest/online/dmalloc_27.html
也可以用dmalloc這個命令來設(shè)置。直接dmalloc -v可用于查看當(dāng)前設(shè)置。

發(fā)生錯誤時會給出類似以下輸出:

1434270937: 2:   error details: checking user pointer
1434270937: 2:   pointer '0x7fc235336808' from 'unknown' prev access 'problem.cpp:35'
1434270937: 2: ERROR: _dmalloc_chunk_heap_check: free space has been overwritten (err 67)
1434270937: 2:   error details: checking pointer admin
1434270937: 2:   pointer '0x7fc235336808' from 'problem.cpp:37' prev access 'problem.cpp:35'
1434270937: 2: ERROR: free: free space has been overwritten (err 67)

1434271030: 3:   error details: finding address in heap
1434271030: 3:   pointer '0x7f0a7e29d808' from 'problem.cpp:27' prev access 'unknown'
1434271030: 3: ERROR: free: tried to free previously freed pointer (err 61)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

另外Dmalloc還提供一些函數(shù),如dmalloc_mark(),dmalloc_log_changed()和dmalloc_log_unfreed()等來打印內(nèi)存信息和分析內(nèi)存變化:
http:///docs/5.3.0/online/dmalloc_13.html

Dr. Memory

重量級內(nèi)存監(jiān)測工具之一,用于檢測如未初始化內(nèi)存訪問,越界訪問,已釋放內(nèi)存訪問,double free,memory leak以及Windows上的handle leak, GDI API usage error等。它支持Windows, Linux和Mac操作系統(tǒng), IA-32和AMD64平臺,和其它基于binary instrumentation的工具一樣,它不需要改目標(biāo)程序的binary。有個缺點是目前只針對x86上的32位程序。貌似目前正在往ARM上port。其優(yōu)點是對程序的正常執(zhí)行影響小,和Valgrind相比,性能更好。官網(wǎng)為http://www./。Dr. Memory基于DynamioRIO Binary Translator。原始代碼不會直接運(yùn)行,而是會經(jīng)過translation后生成code cache,這些code cache會調(diào)用shared instrumentation來做內(nèi)存檢測。

Dr. Memory提供各平臺的包下載。
https://github.com/DynamoRIO/drmemory/wiki/Downloads
下載后即可直接使用。首先編譯要檢測的測試程序:
$ g++ -m32 -g -Wall problem.cpp -o bug -fno-inline -fno-omit-frame-pointer
(在64位host上編譯32位程序需要安裝libc6-dev-i386和g++-multilib)
然后把Dr.Memory的bin加入PATH,如:
$ export PATH=/home/jzj/tools/DrMemory-Linux-1.8.0-8/bin:$PATH
之后就可以使用Dr.Memory啟動目標(biāo)程序:
\$ drmemory – ./bug
更多用法參見 drmemory -help或http:///docs/page_options.html。

像遇到double-free和heap overflow問題的話就會給出類似下面結(jié)果:

~~Dr.M~~ 
~~Dr.M~~ Error #1: INVALID HEAP ARGUMENT to free 0x08ceb0e8
~~Dr.M~~ # 0 replace_free               [/work/drmemory_package/common/alloc_replace.c:2503]
~~Dr.M~~ # 1 double_free                [/home/jzj/code/problem.cpp:23]
~~Dr.M~~ # 2 main                       [/home/jzj/code/problem.cpp:157]
~~Dr.M~~ Note: @0:00:00.127 in thread 26159
~~Dr.M~~ Note: memory was previously freed here:
~~Dr.M~~ Note: # 0 replace_free               [/work/drmemory_package/common/alloc_replace.c:2503]
~~Dr.M~~ Note: # 1 double_free                [/home/jzj/code/problem.cpp:22]
~~Dr.M~~ Note: # 2 main                       [/home/jzj/code/problem.cpp:157]

~~Dr.M~~ 
~~Dr.M~~ Error #1: UNADDRESSABLE ACCESS beyond heap bounds: writing 0x0988f508-0x0988f509 1 byte(s)
~~Dr.M~~ # 0 overrun               [/home/jzj/code/problem.cpp:32]
~~Dr.M~~ # 1 main                  [/home/jzj/code/problem.cpp:154]
~~Dr.M~~ Note: @0:00:00.099 in thread 26191
~~Dr.M~~ Note: prev lower malloc:  0x0988f0e8-0x0988f4e8
~~Dr.M~~ Note: instruction: mov    $0x6a -> (%eax)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Stack protection

前面的工具大多用于堆內(nèi)存檢錯,對于棧內(nèi)存GCC本身提供了一些檢錯機(jī)制。加上-fstack-protector后,GCC會多加指令來檢查buffer/stack overflow。原理是為函數(shù)加guard variable。在函數(shù)進(jìn)入時初始化,函數(shù)退出時檢查。相關(guān)的flag有-fstack-protector-strong -fstack-protector -fstack-protector-all等。使用例子:
$ g++ -Wall -O2 -U_FORTIFY_SOURCE -fstack-protector-all problem.cpp -o bug
運(yùn)行時會檢測到stack overflow:

*** stack smashing detected ***: ./bug terminated
Aborted (core dumped)
  • 1
  • 2

對于線程的棧可以參考pthread_attr_setguardsize()。

Rational purify & Insure++

Rational purity是IBM的商業(yè)化產(chǎn)品,要收費(fèi),所以木有用過,精神上支持。和Valgrind很像,也基于binary instrumentation,適用于不同平臺。另一個工具Insure++基于compile-time和binary instrumentation,可以檢測use-after-free,out-of-bounds,wild free和memory leak等內(nèi)存問題。但也是要收費(fèi)的,也精神上支持。。。。。。

大體來說,遇到詭異的內(nèi)存問題,先可以試下Glibc和GCC里自帶的檢測機(jī)制,因為enable起來方便。如果檢測不出來,那如果toolchain版本較新且有編譯環(huán)境,可以先嘗試ASan,因為其功能強(qiáng)大,且效率高。接下來,如果程序是I/O bound或slowdown可以接受,可以用Valgrind和Dr.Memory。它們功能強(qiáng)大且無需重新編譯,但速度較慢,且后者不支持64位程序和ARM平臺。然后可以根據(jù)實際情況和具體需要考慮Memwatch,Dmalloc和Electric Fence等工具。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多