使用mprotect定位踩記憶體故障

2021-10-17 10:49:09 字數 3021 閱讀 8946

對於 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 已經踩記憶體 堆記憶體 ...