當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > C語(yǔ)言與CPP編程
[導(dǎo)讀]1 引言 “緩沖區(qū)溢出”對(duì)現(xiàn)代操作系統(tǒng)與編譯器來(lái)講已經(jīng)不是什么大問(wèn)題,但是作為一個(gè)合格的 C/C++ 程序員,還是完全有必要了解它的整個(gè)細(xì)節(jié)。 計(jì)算機(jī)程序一般都會(huì)使用到一些內(nèi)存,這些內(nèi)存或是程序內(nèi)部使用,或是存放用戶的輸入數(shù)據(jù),這樣的內(nèi)存一般稱作緩沖

1 引言

緩沖區(qū)溢出”對(duì)現(xiàn)代操作系統(tǒng)與編譯器來(lái)講已經(jīng)不是什么大問(wèn)題,但是作為一個(gè)合格的 C/C++ 程序員,還是完全有必要了解它的整個(gè)細(xì)節(jié)。

計(jì)算機(jī)程序一般都會(huì)使用到一些內(nèi)存,這些內(nèi)存或是程序內(nèi)部使用,或是存放用戶的輸入數(shù)據(jù),這樣的內(nèi)存一般稱作緩沖區(qū)。簡(jiǎn)單的說(shuō),緩沖區(qū)就是一塊連續(xù)的計(jì)算機(jī)內(nèi)存區(qū)域,它可以保存相同數(shù)據(jù)類(lèi)型的多個(gè)實(shí)例,如字符數(shù)組。而緩沖區(qū)溢出則是指當(dāng)計(jì)算機(jī)向緩沖區(qū)內(nèi)填充數(shù)據(jù)位數(shù)時(shí)超過(guò)了緩沖區(qū)本身的容量,溢出的數(shù)據(jù)覆蓋在合法數(shù)據(jù)上。

2 C/C++中內(nèi)存分配

任何一個(gè)源程序通常都包括靜態(tài)的代碼段(或者稱為文本段)和靜態(tài)的數(shù)據(jù)段,為了運(yùn)行程序,操作系統(tǒng)首先負(fù)責(zé)為其創(chuàng)建進(jìn)程,并在進(jìn)程的虛擬地址空間中為其代碼段和數(shù)據(jù)段建立映射。但是只有靜態(tài)的代碼段和數(shù)據(jù)段是不夠的,進(jìn)程在運(yùn)行過(guò)程中還要有其動(dòng)態(tài)環(huán)境。

一般說(shuō)來(lái),默認(rèn)的動(dòng)態(tài)存儲(chǔ)環(huán)境通過(guò)堆棧機(jī)制建立。所有局部變量及所有按值傳遞的函數(shù)參數(shù)都通過(guò)堆棧機(jī)制自動(dòng)分配內(nèi)存空間。如下圖。

程序在內(nèi)存的映射
  • 棧區(qū)(stack):由編譯器自動(dòng)分配與釋放,存放為運(yùn)行時(shí)函數(shù)分配的局部變量、函數(shù)參數(shù)、返回?cái)?shù)據(jù)、返回地址等。其操作類(lèi)似于數(shù)據(jù)結(jié)構(gòu)中的棧。
  • 堆區(qū)(heap):一般由程序員自動(dòng)分配,如果程序員沒(méi)有釋放,程序結(jié)束時(shí)可能有OS回收。其分配類(lèi)似于鏈表。
  • 全局區(qū)(靜態(tài)區(qū)static):數(shù)據(jù)段,程序結(jié)束后由系統(tǒng)釋放。全局區(qū)分為已初始化全局區(qū)(data),用來(lái)存放保存全局的和靜態(tài)的已初始化變量和未初始化全局區(qū)(bss),用來(lái)保存全局的和靜態(tài)的未初始化變量。
  • 常量區(qū)(文字常量區(qū)):數(shù)據(jù)段,存放常量字符串,程序結(jié)束后有系統(tǒng)釋放。
  • 代碼區(qū):存放函數(shù)體(類(lèi)成員函數(shù)和全局區(qū))的二進(jìn)制代碼,這個(gè)段在內(nèi)存中一般被標(biāo)記為只讀,任何對(duì)該區(qū)的寫(xiě)操作都會(huì)導(dǎo)致段錯(cuò)誤(Segmentation Fault)。

需要特別注意的是,堆(Heap)和棧(Stack)是有區(qū)別的,很多程序員混淆堆棧的概念,或者認(rèn)為它們就是一個(gè)概念。簡(jiǎn)單來(lái)說(shuō),它們之間的主要區(qū)別可以表現(xiàn)在如下五個(gè)方面。

分配和管理方式不同

堆是動(dòng)態(tài)分配的,其空間的分配和釋放都由程序員控制。也就是說(shuō),堆的大小并不固定,可動(dòng)態(tài)擴(kuò)張或縮減,其分配由malloc()等這類(lèi)實(shí)時(shí)內(nèi)存分配函數(shù)來(lái)實(shí)現(xiàn)。當(dāng)進(jìn)程調(diào)用malloc等函數(shù)分配內(nèi)存時(shí),新分配的內(nèi)存就被動(dòng)態(tài)添加到堆上(堆被擴(kuò)張);當(dāng)利用free等函數(shù)釋放內(nèi)存時(shí),被釋放的內(nèi)存從堆中被剔除(堆被縮減)。

而棧由編譯器自動(dòng)管理,其分配方式有兩種:靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配由編譯器完成,比如局部變量的分配。動(dòng)態(tài)分配由alloca()函數(shù)進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的,它的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放,無(wú)需手工控制。

申請(qǐng)的大小限制不同

棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存區(qū)域,棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,能從棧獲得的空間較小。

堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域,這是由于系統(tǒng)是由鏈表在存儲(chǔ)空閑內(nèi)存地址,自然堆就是不連續(xù)的內(nèi)存區(qū)域,且鏈表的遍歷也是從低地址向高地址遍歷的,堆的大小受限于計(jì)算機(jī)系統(tǒng)的有效虛擬內(nèi)存空間,

由此空間,堆獲得的空間比較靈活,也比較大。在 32 位平臺(tái)下,VC6 下默認(rèn)為 1M,堆最大可以到 4G;

申請(qǐng)效率不同

  • 棧由系統(tǒng)自動(dòng)分配,速度快,但是程序員無(wú)法控制。
  • 堆是有程序員自己分配,速度較慢,容易產(chǎn)生碎片,不過(guò)用起來(lái)方便。

產(chǎn)生碎片不同

對(duì)堆來(lái)說(shuō),頻繁執(zhí)行malloc或free勢(shì)必會(huì)造成內(nèi)存空間的不連續(xù),形成大量的碎片,使程序效率降低;而對(duì)棧而言,則不存在碎片問(wèn)題。

內(nèi)存地址增長(zhǎng)的方向不同

  • 堆是向著內(nèi)存地址增加的方向增長(zhǎng)的,從內(nèi)存的低地址向高地址方向增長(zhǎng);
  • 棧的增長(zhǎng)方向與之相反,是向著內(nèi)存地址減小的方向增長(zhǎng),由內(nèi)存的高地址向低地址方向增長(zhǎng)。

假設(shè)一個(gè)程序的函數(shù)調(diào)用順序?yàn)椋褐骱瘮?shù)main調(diào)用函數(shù)func1,函數(shù)func1調(diào)用函數(shù)func2。當(dāng)這個(gè)程序被操作系統(tǒng)調(diào)入內(nèi)存運(yùn)行時(shí),其對(duì)應(yīng)的進(jìn)程在內(nèi)存中的映射結(jié)果如下圖所示

例子中的內(nèi)存映射

進(jìn)程的棧是由多個(gè)棧幀構(gòu)成的,其中每個(gè)棧幀都對(duì)應(yīng)一個(gè)函數(shù)調(diào)用。當(dāng)調(diào)用函數(shù)時(shí),新的棧幀被壓入棧;當(dāng)函數(shù)返回時(shí),相應(yīng)的棧幀從棧中彈出。由于需要將函數(shù)返回地址這樣的重要數(shù)據(jù)保存在程序員可見(jiàn)的堆棧中,因此也給系統(tǒng)安全帶來(lái)了極大的隱患。

當(dāng)程序?qū)懭氤^(guò)緩沖區(qū)的邊界時(shí),就會(huì)產(chǎn)生所謂的“緩沖區(qū)溢出”。發(fā)生緩沖區(qū)溢出時(shí),就會(huì)覆蓋下一個(gè)相鄰的內(nèi)存塊,導(dǎo)致程序發(fā)生一些不可預(yù)料的結(jié)果:也許程序可以繼續(xù),也許程序的執(zhí)行出現(xiàn)奇怪現(xiàn)象,也許程序完全失敗或者崩潰等。

緩沖區(qū)溢出

對(duì)于緩沖區(qū)溢出,一般可以分為4種類(lèi)型,即棧溢出、堆溢出、BSS溢出與格式化串溢出。其中,棧溢出是最簡(jiǎn)單,也是最為常見(jiàn)的一種溢出方式。

沒(méi)有保證足夠的存儲(chǔ)空間存儲(chǔ)復(fù)制過(guò)來(lái)的數(shù)據(jù)

void function(char *str) 
{
   char buffer[10];
   strcpy(buffer,str);
}

上面的strcpy()將直接把str中的內(nèi)容copybuffer中。這樣只要str的長(zhǎng)度大于 10 ,就會(huì)造成buffer的溢出,使程序運(yùn)行出錯(cuò)。存在象strcpy這樣的問(wèn)題的標(biāo)準(zhǔn)函數(shù)還有strcat(),sprintf(),vsprintf(),gets(),scanf()等。對(duì)應(yīng)的有更加安全的函數(shù),即在函數(shù)名后加上_s,如scanf_s()函數(shù)。

  • 嚴(yán)格檢查輸入長(zhǎng)度和緩沖區(qū)長(zhǎng)度。
  • 常見(jiàn)的高危函數(shù)
函數(shù) 嚴(yán)重性 防范手段
gets() 最危險(xiǎn) 使用 fgets(buf, size, stdin)
strcpy() 很危險(xiǎn) 改為使用 strncpy()
strcat() 很危險(xiǎn) 改為使用 strncat()
sprintf() 很危險(xiǎn) 改為使用snprintf(),或者使用精度說(shuō)明符
scanf() 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析
sscanf() 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析
fscanf() 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析
vfscanf() 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析
vfscanf() 很危險(xiǎn) 改為使用 vsnprintf(),或者使用精度說(shuō)明符
vscanf() 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析
vsscanf() 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析
streadd() 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析

整數(shù)溢出

  1. 寬度溢出:把一個(gè)寬度較大的操作數(shù)賦給寬度較小的操作數(shù),就有可能發(fā)生數(shù)據(jù)截?cái)嗷蚍?hào)位丟失
#include<stdio.h>

int main()
{
  signed int value1 = 10;
  usigned int value2 = (unsigned int)value1;
}
  1. 算術(shù)溢出,該程序即使在接受用戶輸入的時(shí)候?qū)、b的賦值做安全性檢查,a+b 依舊可能溢出:
#include<stdio.h>

int main()
{
  int a;
  int b;
  int c=a*b;
  return 0;
}

數(shù)組索引不在合法范圍內(nèi)

enum {TABLESIZE = 100};
int *table = NULL;
int insert_in_table(int pos, int value) {
  if(!table) {
    table = (int *)malloc(sizeof(int) *TABLESIZE);
  }
  if(pos >= TABLESIZE) {
    return -1;
  }
  table[pos] = value;
  return 0;
}

其中:posint類(lèi)型,可能為負(fù)數(shù),這會(huì)導(dǎo)致在數(shù)組所引用的內(nèi)存邊界之外進(jìn)行寫(xiě)入,可以將pos類(lèi)型改為size_t避免

空字符錯(cuò)誤

例如:

//錯(cuò)誤
char array[]={'0','1','2','3','4','5','6','7','8'};
//正確的寫(xiě)法應(yīng)為:
char array[]={'0','1','2','3','4','5','6','7','8',’\0’};
//或者
char array[11]={'0','1','2','3','4','5','6','7','8','9’};


點(diǎn)【在看】是最大的支持 

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉