寫入到a時緩衝區溢位 一文理解緩衝區溢位

2021-10-18 07:56:14 字數 3249 閱讀 5194

「緩衝區溢位」對現代作業系統與編譯器來講已經不是什麼大問題,但是作為乙個合格的 c/c++ 程式設計師,還是完全有必要了解它的整個細節。

電腦程式一般都會使用到一些記憶體,這些記憶體或是程式內部使用,或是存放使用者的輸入資料,這樣的記憶體一般稱作緩衝區。簡單的說,緩衝區就是一塊連續的計算機記憶體區域,它可以儲存相同資料型別的多個例項,如字元陣列。而緩衝區溢位則是指當計算機向緩衝區內填充資料位數時超過了緩衝區本身的容量,溢位的資料覆蓋在合法資料上。

任何乙個源程式通常都包括靜態的**段(或者稱為文字段)和靜態的資料段,為了執行程式,作業系統首先負責為其建立程序,並在程序的虛擬位址空間中為其**段和資料段建立對映。但是只有靜態的**段和資料段是不夠的,程序在執行過程中還要有其動態環境。

一般說來,預設的動態儲存環境通過堆疊機制建立。所有區域性變數及所有按值傳遞的函式引數都通過堆疊機制自動分配記憶體空間,分配同一資料型別相鄰塊的記憶體區域被稱為緩衝區。如下圖。

程式在記憶體的對映

堆區(heap):一般由程式設計師自動分配,如果程式設計師沒有釋放,程式結束時可能有os**。其分配類似於鍊錶。

全域性區(靜態區static):資料段,程式結束後由系統釋放。全域性區分為已初始化全域性區(data),用來存放儲存全域性的和靜態的已初始化變數和未初始化全域性區(bss),用來儲存全域性的和靜態的未初始化變數。

常量區(文字常量區):資料段,存放常量字串,程式結束後有系統釋放。

**區:存放函式體(類成員函式和全域性區)的二進位制**,這個段在記憶體中一般被標記為唯讀,任何對該區的寫操作都會導致段錯誤(segmentation fault)。

需要特別注意的是,堆(heap)和棧(stack)是有區別的,很多程式設計師混淆堆疊的概念,或者認為它們就是乙個概念。簡單來說,它們之間的主要區別可以表現在如下五個方面。

堆是動態分配的,其空間的分配和釋放都由程式設計師控制。也就是說,堆的大小並不固定,可動態擴張或縮減,其分配由malloc()等這類實時記憶體分配函式來實現。當程序呼叫malloc等函式分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張);當利用free等函式釋放記憶體時,被釋放的記憶體從堆中被剔除(堆被縮減)。

而棧由編譯器自動管理,其分配方式有兩種:靜態分配和動態分配。靜態分配由編譯器完成,比如區域性變數的分配。動態分配由alloca()函式進行分配,但是棧的動態分配和堆是不同的,它的動態分配是由編譯器進行釋放,無需手工控制。

棧是向低位址擴充套件的資料結構,是一塊連續的記憶體區域,棧頂的位址和棧的最大容量是系統預先規定好的,能從棧獲得的空間較小。

堆是向高位址擴充套件的資料結構,是不連續的記憶體區域,這是由於系統是由鍊錶在儲存空閒記憶體位址,自然堆就是不連續的記憶體區域,且鍊錶的遍歷也是從低位址向高位址遍歷的,堆得大小受限於計算機系統的有效虛擬記憶體空間,

由此空間,堆獲得的空間比較靈活,也比較大。在 32 位平台下,vc6 下預設為 1m,堆最大可以到 4g;

對堆來說,頻繁執行malloc或free勢必會造成記憶體空間的不連續,形成大量的碎片,使程式效率降低;而對棧而言,則不存在碎片問題。

假設乙個程式的函式呼叫順序為:主函式main呼叫函式func1,函式func1呼叫函式func2。當這個程式被作業系統調入記憶體執行時,其對應的程序在記憶體中的對映結果如下圖所示

例子中的記憶體對映

程序的棧是由多個棧幀構成的,其中每個棧幀都對應乙個函式呼叫。當呼叫函式時,新的棧幀被壓入棧;當函式返回時,相應的棧幀從棧中彈出。由於需要將函式返回位址這樣的重要資料儲存在程式設計師可見的堆疊中,因此也給系統安全帶來了極大的隱患。

當程式寫入超過緩衝區的邊界時,就會產生所謂的「緩衝區溢位」。發生緩衝區溢位時,就會覆蓋下乙個相鄰的記憶體塊,導致程式發生一些不可預料的結果:也許程式可以繼續,也許程式的執行出現奇怪現象,也許程式完全失敗或者崩潰等。

對於緩衝區溢位,一般可以分為4種型別,即棧溢位、堆溢位、bss溢位與格式化串溢位。其中,棧溢位是最簡單,也是最為常見的一種溢位方式。

void function(char *str)
上面的strcpy()將直接把str中的內容copy到buffer中。這樣只要str的長度大於 10 ,就會造成buffer的溢位,使程式執行出錯。存在象strcpy這樣的問題的標準函式還有strcat(),sprintf(),vsprintf(),gets(),scanf()等。對應的有更加安全的函式,即在函式名後加上_s,如scanf_s()函式。

函式嚴重性防範手段gets() 最危險 使用 fgets(buf, size, stdin) strcpy() 很危險 改為使用 strncpy() strcat() 很危險 改為使用 strncat() sprintf() 很危險 改為使用snprintf(),或者使用精度說明符 scanf() 很危險 使用精度說明符,或自己進行解析 sscanf() 很危險 使用精度說明符,或自己進行解析 fscanf() 很危險 使用精度說明符,或自己進行解析 vfscanf() 很危險 使用精度說明符,或自己進行解析 vfscanf() 很危險 改為使用 vsnprintf(),或者使用精度說明符 vscanf() 很危險 使用精度說明符,或自己進行解析 vsscanf() 很危險 使用精度說明符,或自己進行解析 streadd() 很危險 使用精度說明符,或自己進行解析

寬度溢位:把乙個寬度較大的運算元賦給寬度較小的運算元,就有可能發生資料截斷或符號位丟失

#includeint main()
算術溢位,該程式即使在接受使用者輸入的時候對a、b的賦值做安全性檢查,a+b依舊可能溢位:

#includeint main()
enum ;int *table = null;int insert_in_table(int pos, int value)   if(pos >= tablesize)   table[pos] = value;  return 0;}
其中:pos為int型別,可能為負數,這會導致在陣列所引用的記憶體邊界之外進行寫入,可以將pos型別改為size_t避免

例如:

//錯誤char array=;//正確的寫法應為:char array=;//或者char array[11]=;

緩衝區溢位

緩衝區溢位是指當電腦程式向緩衝區內填充的資料位數超過了緩衝區本身的容量。溢位的資料覆蓋在合法資料上。理想情況是,程式檢查資料長度並且不允許輸入超過緩衝區長度的字串。但是絕大多數程式都會假設資料長度總是與所分配的儲存空間相匹配,這就為緩衝區溢位埋下隱患。作業系統所使用的緩衝區又被稱為堆疊,在各個操作程...

緩衝區溢位

緩衝區溢位,簡單的說就是計算機對接收的輸入資料沒有進行有效的檢測 理想的情況是程式檢查資料長度並不允許輸入超過緩衝區長度的字元 向緩衝區內填充資料時超過了緩衝區本身的容量,而導致資料溢位到被分配空間之外的記憶體空間,使得溢位的資料覆蓋了其他記憶體空間的資料。通過往程式的緩衝區寫超出其長度的內容,造成...

緩衝區溢位 棧溢位

1 緩衝區概念 緩衝區 buffer 又稱為快取,它是記憶體空間的一部分。也就是說,在記憶體空間中預留了一定的儲存空間,這些儲存空間用來緩衝輸入或輸出的資料,這部分預留的空間就叫做緩衝區。使用緩衝區有兩個好處 1 減少實際物理讀寫次數 2 緩衝區在建立時就被分配記憶體,這塊記憶體區域一直被重用,可以...