Sanitizer簡介
Sanitizer是由Google發起的開源工具集,用于檢測內存泄露等(deng)問題。
鏈接://github.com/google/sanitizers/wiki/
它包(bao)括(kuo)了(le)AddressSanitizer、MemorySanitizer、ThreadSanitizer、LeakSanitizer等多(duo)種工具(ju)。這(zhe)些工具(ju)最初是(shi)LLVM項目的(de)一部分,后(hou)來也被GNU的(de)GCC編譯器支持(chi)。從GCC的(de)4.8版本(ben)開始(shi),就已經支持(chi)AddressSanitizer和ThreadSanitizer,而4.9版本(ben)則開始(shi)支持(chi)LeakSanitizer。
Sanitizer使用(yong)
1、AddressSanitizer的使用例(li)子
AddressSanitizer(ASan) 是一個快速(su)內存檢測(ce)器,可以檢測(ce)出(chu)緩沖區溢出(chu)、使用(yong)已釋放(fang)內存等問(wen)題。編(bian)譯時帶(dai)上參數(shu) -fsanitize=address及-g。
(1)捕捉棧緩(huan)沖區(qu)溢出(chu)問(wen)題:
AddressSanitizer.c:
// 微信公眾號:嵌入式大雜燴
#include
voidtest_func(void)
{
int a[6] = {0};
int b = a[6]; // 棧緩沖區溢出
}
intmain(int argc, char **argv)
{
test_func();
return0;
}
編譯、運行:
gcc AddressSanitizer.c -fsanitize=address -g -o AddressSanitizer
執行結果分析:
觸發(fa)了檢測錯誤級別,終止程序并給出了程序運行異常的原因及異常的代碼位置。
(2)捕捉使(shi)用已釋放(fang)內存問題:
ThreadSanitizer.c:
// 微信公眾號:嵌入式大雜燴
#include
voidtest_func(void)
{
char *p = malloc(10);
p[0] = 1;
free(p);
p[0] = 1; // 使用已釋放內存
}
intmain(int argc, char **argv)
{
test_func();
return0;
}
2、ThreadSanitizer的使用例(li)子
ThreadSanitizer(TSan) 是一(yi)個(ge)數據(ju)競爭檢測器,可(ke)以用來分析線(xian)程競態(tai)、死鎖等線(xian)程相關問題。編(bian)譯時(shi)帶上參數 -fsanitize=thread及(ji)-g。
捕捉 線程間數(shu)據競爭(zheng) 問題:
// 微信公眾號:嵌入式大雜燴
#include
#include
int g_counter = 0; // thread1、thread2競爭的數據
void *increment(void *arg)
{
g_counter++;
}
void *decrement(void *arg)
{
g_counter--;
}
voidtest_func(void)
{
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, increment, NULL);
pthread_create(&thread2, NULL, decrement, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Counter value: %d\n", g_counter);
}
intmain(int argc, char **argv)
{
test_func();
return0;
}
編(bian)譯、運行:
gcc ThreadSanitizer.c -fsanitize=thread -g -pthread -o ThreadSanitizer
執行結果分析:
觸(chu)發了檢測警告級別(bie),程序(xu)仍能運行,并給出了程序(xu)運行有風(feng)險(xian)的(de)原因及(ji)有風(feng)險(xian)的(de)代(dai)碼位置(zhi)。
3、程序中(zhong)同(tong)時存(cun)在多處(chu)風(feng)險?
上面的(de)例子分別(bie)使(shi)用(yong)AddressSanitizer檢(jian)測(ce)(ce)器與(yu)ThreadSanitizer檢(jian)測(ce)(ce)器來檢(jian)測(ce)(ce)對應的(de)異常,可以較為精準(zhun)地檢(jian)測(ce)(ce)到對應的(de)異常。
如果程序中(zhong)同時存在多處風險呢(ni)?
這也是比較貼近我(wo)(wo)們(men)(men)的(de)實際(ji)應(ying)用的(de),畢竟我(wo)(wo)們(men)(men)并不知(zhi)道(dao)我(wo)(wo)們(men)(men)的(de)代碼里有哪(na)些可能(neng)存在的(de)風險。這種情(qing)況(kuang)我(wo)(wo)們(men)(men)要(yao)怎么檢測?
編譯(yi)時能同時帶上多(duo)個-fsanitize參數調用多(duo)個檢(jian)測器嗎?
可以(yi)同(tong)時帶,但有些檢測器(qi)不能同(tong)時使(shi)用。
AddressSanitizer與ThreadSanitizer檢測器不(bu)能同時使用。
但是,假如我們的程序中(zhong)恰好存在(zai)address異常(chang)(chang)與thread異常(chang)(chang)呢(ni),單獨使用AddressSanitizer檢測器(qi)、ThreadSanitizer檢測器(qi)的表現是怎樣的?
比如(ru),我們把上(shang)面(mian)3個例子的代碼(ma)放在一起:
test.c:
// 微信公眾號:嵌入式大雜燴
#include
#include
#include
int g_counter = 0; // thread1、thread2競爭的數據
void *increment(void *arg)
{
g_counter++;
}
void *decrement(void *arg)
{
g_counter--;
}
// 測試:資源競爭
voidtest_func(void)
{
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, increment, NULL);
pthread_create(&thread2, NULL, decrement, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Counter value: %d\n", g_counter);
}
// 測試:使用已釋放內存
voidtest_func1(void)
{
char *p = malloc(10);
printf("This is test_func1\n");
p[0] = 1;
free(p);
p[0] = 1; // 使用已釋放內存
}
// 測試:棧緩沖區溢出
voidtest_func2(void)
{
int a[6] = {0};
int b = a[6]; // 棧緩沖區溢出
}
intmain(int argc, char **argv)
{
test_func();
test_func1();
test_func2();
return0;
}
帶-fsanitize=thread參數編譯(yi)、運行:
執行結果分析:
ThreadSanitizer檢測器(qi)能正常檢測出資(zi)源競爭的問題,也檢測出了(le)test_func1中(zhong)的使用(yong)已釋放的(de)堆(dui)內存的問題并以(yi)警(jing)告級別(bie)報告,但沒有檢測(ce)出test_func2的棧(zhan)緩沖區溢出問題。
是不是因為test_func2運行在test_func1后(hou)面了,所(suo)以test_func2的(de)異常沒有被ThreadSanitizer檢測(ce)器檢測(ce)出來?
我們調換(huan)個位置看(kan)看(kan):
intmain(int argc, char **argv)
{
test_func();
test_func2();
test_func1();
return0;
}
顯然(ran),執行結果還(huan)是(shi)一樣的,test_func2的棧緩(huan)沖區溢出問題還是沒有被ThreadSanitizer檢測(ce)器檢測(ce)出來(lai)。
所以,大致得(de)出結論:當(dang)程序(xu)里存在thread異常(chang)與address異常(chang)時,使(shi)用ThreadSanitizer檢(jian)測器(qi)能準確檢(jian)測到thread異常(chang),能檢(jian)測到部(bu)分address異常(chang)。
帶-fsanitize=address參數編譯、運行:
執(zhi)行順序(xu):
intmain(int argc, char **argv)
{
test_func();
test_func1();
test_func2();
return0;
}
執行結(jie)果分析:
AddressSanitizer檢(jian)(jian)測器檢(jian)(jian)測到了test_func1中的已使用釋放的堆(dui)內存(cun)的(de)異常(chang)并(bing)(bing)以(yi)錯誤級別(bie)報告,并(bing)(bing)終止了(le)程序(xu);沒(mei)有(you)檢測到(dao)(dao)test_func的(de)資源競爭的(de)風險(xian);也(ye)沒(mei)有(you)檢測到(dao)(dao)test_func2的(de)棧緩沖區溢出的(de)問題,因為執行(xing)到(dao)(dao)test_func1的(de)時候程序(xu)已經被終止了(le),如果(guo)把test_func2放在test_func1之(zhi)前運行(xing),就能(neng)檢測到(dao)(dao)test_func2的(de)異常(chang)。
結(jie)論:當程(cheng)序里(li)存在thread異常(chang)與address異常(chang)時,使用AddressSanitizer檢測(ce)器能(neng)準(zhun)確檢測(ce)到第(di)一個觸發(fa)的address異常(chang),不能(neng)檢測(ce)到thread異常(chang)。
如果程(cheng)序中存(cun)在(zai)多種可能存(cun)在(zai)的風險時,需要使用多個(ge)檢(jian)測(ce)(ce)器(qi)(qi)單獨挨個(ge)檢(jian)測(ce)(ce)。每(mei)個(ge)檢(jian)測(ce)(ce)器(qi)(qi)都有其擅長(chang)檢(jian)測(ce)(ce)的方(fang)面(mian),可以經(jing)過(guo)初步分析之后(hou)確定大致地方(fang)向,選擇(ze)適(shi)合地檢(jian)測(ce)(ce)器(qi)(qi)來做檢(jian)測(ce)(ce)。
以(yi)上(shang)就(jiu)是(shi)關于(yu)Sanitizer的一些(xie)簡單(dan)介(jie)紹及(ji)使(shi)用(yong)的分享,更多的關于(yu)Sanitizer的資料可查閱://github.com/google/sanitizers/wiki/
碼字不易,如(ru)果文(wen)章(zhang)對你(ni)有幫助(zhu)!