對於 c 語言來說,記憶體被踩是比較常見的問題,輕則普通變數被改寫程式邏輯出錯,重則指標變數被改寫引發指標解引用出現未定義行為風險;
定位記憶體被踩一直是棘手的難題,如果出現程式跑死,一般可以通過堆疊資訊來定位:
1)檢視跑死的呼叫鏈,確定跑死**的位置;
2)根據pc指標找到具體**;
3)走查**分析問題;
但是這種方法有個先天的劣勢:程式跑死的點和記憶體被踩的點往往不在同乙個地方,需要分析**尋找真正的問題點。如果程式只是邏輯出錯沒有跑死,定位起來會更加困難。
有沒有方法可以讓程式告訴我們是誰踩了記憶體呢?
這裡分享一種借助 mprotect 函式定位記憶體被踩的方法。
mprotect是 linux 系統中用於修改一段指定記憶體區域保護屬性的函式,其原型是:
#include
#include
intmprotect
(const
void
* start, size_t len,
int prot)
其中start是被保護記憶體的起始位址,len是被保護記憶體的長度,prot是記憶體的保護屬性,常見的屬性有:
保護屬性
說明prot_read
記憶體可讀
prot_write
記憶體可寫
prot_exec
記憶體可執行
prot_none
記憶體不可訪問
需要注意的是,mprotect函式在使用上有限制:
關於記憶體頁的這裡不多做介紹,有興趣的可以看看其他博文的介紹,需要知道的是一般記憶體頁是按 4096 位元組(4kb)為單位對齊的。
下面以乙個實際的例子來說明mprotect的使用方法。
定義以下結構體、變數和函式:
#define max_array_size (4096)
typedef
struct subinst
subinst;
typedef
struct inst
inst;
inst* ginst =
null
;void
createinst()
void
dosomething()
void
printinst()
intmain()
很容易就可以看出在dosomething()函式中由於指標偏移錯誤,改寫了指標 subinst 的值為 0, 所以在printinst()中列印時出現空指標訪問,引起程式跑死。
根據呼叫鏈可以得到以下段錯誤資訊:
顯然,根據 coredump 資訊只能看到程式跑死在 subinst 解引用時出現問題。
如果提示缺少 glibc 但安裝不上,需要修改一下 etc/yum.repos.d/centos-linux-debuginfo.repo 中enabled 的值為1。
coredump 資訊缺失的話請檢查 ulimit -c,可以修改 etc/profile,新增 ulimit -s -c 0 > /dev/null 2>&1,記得 source etc/profile;下面來看看mprotect是如何幫助我們找到問題點的。
首先改寫**如下
#define max_array_size (4096)
typedef
struct subinst
subinst;
typedef
struct inst
inst;
inst* ginst =
null
;void
createinst()
void
dosomething()
void
printinst()
intmain()
解釋一下幾個關鍵點
sysconf(_sc_pagesize) 返回當前作業系統的記憶體頁大小,一般是 4096 位元組;
#include
intposix_memalign
(void
** memptr, size_t alignment, size_t size)
;
其中
memptr是個2級指標,指向存放申請記憶體位址的指標變數的指標;
alignment是期望對齊的記憶體長度;
size是申請的記憶體大小。
前面說過,mprotect要求被保護的記憶體是完整的記憶體頁且 4kb 對齊,所以我們在被踩的記憶體 subinst 指標前加入了一段 4kb 大小的記憶體 pzone,並且使用 mprotect 將這段記憶體設定為唯讀。
typedef
struct inst
inst;
再次執行上面的程式,這次程式很直接的就告訴了我們記憶體被踩的案發現場。
上面結合例子分享了一種使用 mprotect 定位被踩的方法,例子舉的比較簡單,所以在一些更為複雜的**中效果會更明顯,核心思想是:
根據不同情況選擇合適的定位方法才是我們需要掌握的技巧,有方法總比沒有方法好:d
mprotect 設定記憶體訪問許可權
mmap 的第三個引數指定對記憶體區域的保護,由標記讀 寫 執行許可權的 prot read prot write 和 prot exec 按位與操作獲得,或者是限制沒有訪問許可權的 prot none。如果程式嘗試在不允許這些許可權的本地記憶體上操作,它將被 sigsegv 訊號 segmenta...
記憶體洩露定位
gflags.exe在windbg安裝目錄下 1 gflags.exe i exe ust 如果設定失敗,說明登錄檔被禁用了,可以嘗試解除所有對登錄檔的禁用。這個登錄檔位置為 hkey local machine software microsoft windows nt currentversio...
踩記憶體的簡單理解
定義 對不屬於你的記憶體進行讀寫叫踩記憶體。分類 根據記憶體分配機制劃分,全域性區 data bss 堆,棧踩的記憶體。全域性區 int array 10 全域性變數 void function for i 0 i 10 i array的範圍為 0 9 這裡array 10 已經踩記憶體 堆記憶體 ...