<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          緩沖區(qū)溢出

          共 3431字,需瀏覽 7分鐘

           ·

          2020-08-19 03:09

          1 引言

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

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

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

          任何一個源程序通常都包括靜態(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)行過程中還要有其動態(tài)環(huán)境。

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

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

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

          分配和管理方式不同

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

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

          申請的大小限制不同

          棧是向低地址擴(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)是由鏈表在存儲空閑內(nèi)存地址,自然堆就是不連續(xù)的內(nèi)存區(qū)域,且鏈表的遍歷也是從低地址向高地址遍歷的,堆的大小受限于計算機(jī)系統(tǒng)的有效虛擬內(nèi)存空間,

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

          申請效率不同

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

          產(chǎn)生碎片不同

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

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

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

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

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

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

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

          緩沖區(qū)溢出

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

          沒有保證足夠的存儲空間存儲復(fù)制過來的數(shù)據(jù)

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

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

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

          整數(shù)溢出

          1. 寬度溢出:把一個寬度較大的操作數(shù)賦給寬度較小的操作數(shù),就有可能發(fā)生數(shù)據(jù)截斷或符號位丟失
          #include

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

          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類型,可能為負(fù)數(shù),這會導(dǎo)致在數(shù)組所引用的內(nèi)存邊界之外進(jìn)行寫入,可以將pos類型改為size_t避免

          空字符錯誤

          例如:

          //錯誤
          char?array[]={'0','1','2','3','4','5','6','7','8'};
          //正確的寫法應(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’};


          點【在看】是最大的支持?

          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青娱乐av免费观看 | 理论亚洲成人电影 | 欧美肏逼网站 | 蜜桃av秘 无码一区二区 | 久久伊人影院 |