美團二面:為什么mmap共享內(nèi)存比malloc危險?
今天來聊個面試題,為什么mmap共享內(nèi)存比malloc更危險,這是一個比較有區(qū)分度的題目。
在系統(tǒng)編程中,內(nèi)存管理是程序員必須面對的核心問題之一。
C/C++開發(fā)者常用的內(nèi)存分配方式包括malloc(堆內(nèi)存)和mmap(共享內(nèi)存/文件映射)。
圖片
雖然兩者都能提供內(nèi)存分配的能力,但從系統(tǒng)風(fēng)險的角度來看,mmap共享內(nèi)存比malloc更加危險,稍有不慎就可能引發(fā)嚴重的安全漏洞或穩(wěn)定性問題。
mmap共享內(nèi)存可能導(dǎo)致其它進程崩潰
mmap可以讓多個進程直接讀寫同一塊物理內(nèi)存,這意味著一個進程的錯誤操作可能直接影響其他進程的數(shù)據(jù),甚至導(dǎo)致崩潰。比如進程A的越界寫入可能導(dǎo)致進程B崩潰或讀取到損壞的數(shù)據(jù)。
一句話,共享內(nèi)存破壞了進程間的內(nèi)存隔離。
圖片
而如果使用malloc,這種越界寫入最多只會影響當前進程,不會跨進程傳播錯誤。因為malloc分配的內(nèi)存僅在當前進程內(nèi)有效,天然具備隔離性,其他進程無法直接訪問。
mmap的權(quán)限風(fēng)險
mmap可以設(shè)置內(nèi)存的讀寫/執(zhí)行權(quán)限(如PROT_EXEC),如果錯誤配置可能被黑客利用進行代碼注入攻擊(如緩沖區(qū)溢出后執(zhí)行惡意代碼)。
假設(shè)有一個網(wǎng)絡(luò)服務(wù)程序,使用 mmap 分配了一塊內(nèi)存用于處理客戶端請求,并錯誤地賦予了可執(zhí)行權(quán)限(PROT_EXEC):
void *mem_start = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
同時,程序?qū)⒂脩糨斎霐?shù)據(jù)(如網(wǎng)絡(luò)數(shù)據(jù)包)復(fù)制到該內(nèi)存區(qū)域,但未檢查輸入長度。
這樣攻擊者就可以構(gòu)造一個超長數(shù)據(jù)包,其中包含惡意機器碼(如 shellcode)和精心設(shè)計的溢出數(shù)據(jù),溢出數(shù)據(jù)覆蓋了程序的返回地址或函數(shù)指針,使其指向 mem_start 的起始地址。
當程序執(zhí)行到該返回地址時,會跳轉(zhuǎn)到 mem_start 區(qū)域。
圖片
由于 mem_start 被錯誤配置為可執(zhí)行(PROT_EXEC),因此操作系統(tǒng)不會阻止該區(qū)域的代碼執(zhí)行。
攻擊者的 shellcode 被成功執(zhí)行,可能實現(xiàn)提權(quán)、反彈 Shell 等惡意操作。
而使用malloc分配的堆內(nèi)存則無此問題。
多線程與同步風(fēng)險
使用mmap共享內(nèi)存時,多進程或線程需通過信號量、鎖等機制同步訪問,否則導(dǎo)致數(shù)據(jù)競爭(Data Race)或臟讀,這顯然會影響所有共享這塊內(nèi)存的進程。
圖片
malloc本身是線程安全的(通過全局鎖或線程本地緩存),但程序員需自行管理分配的內(nèi)存區(qū)域的并發(fā)訪問。若未加鎖,仍可能導(dǎo)致競爭條件,但與mmap的共享內(nèi)存相比,影響范圍限于當前進程內(nèi)。