【linux】Valgrind工具集詳解(九):Memcheck檢查的內容和方法
一、值的有效性
1、什麼是值的有效性?
英文原文是Valid-value (V) bits,直譯過來就是有效值(V)位。
我將它理解為值的有效性,就是判斷在記憶體或CPU的實體地址中儲存的資料是否有效,比如在記憶體中變數(int i)代表的物理位置(不是地址),沒有初始化,就去使用它,是否合法,參見下面的判斷。
2、當僅僅是複製未初始化的值,並且不使用它時,Memcheck不會報告錯誤,認為是有效的。
例子程式碼如下:
int i, j;
int a[10], b[10];
for ( i = 0; i < 10; i++ ) {
j = a[i];
b[i] = j;
}
上述程式碼中陣列a沒有賦值,將陣列a複製給陣列b,雖然這段程式碼沒有意思,但是Memcheck不會報錯。
3、Memcheck會檢查以下三種情況
當使用值生成記憶體地址;
需要進行控制流決策時;
檢測到系統呼叫時。
例子程式碼如下:
for ( i = 0; i < 10; i++ ) {
j += a[i];
}
if ( j == 77 )
printf("hello world!\n");
由於“j”沒有初始化,並且被用到if(控制流決策),所以此處Memcheck會報告錯誤。
二、地址的有效性
英文原文是Valid-address (A) bits:有效地址(A)位。
記憶體的一個物理位置中的資料是否有效,我們稱為值的有效性;是否可以合法地讀取或寫入該位置(即,是否可以訪問該位置),來判斷地址的有效性。
哪些地址有效:
1、程式啟動時,所有全域性資料區域都標記為可訪問(地址有效)。
2、當程式執行 malloc、new,分配的區域標記為可訪問(地址有效),沒有分配的依然時無效的;在釋放該區域後,該區域標記位不可訪問(地址無效)。
3、棧的資料,即區域性變數地址有效。實現方法是,根據棧指標暫存器(SP)的移動來觸發標記哪些是地址有效、哪些已經無效了。規則是從 SP堆疊的底部到堆疊的區域被標記為可訪問,並且下面的區域SP是不可訪問的。
三、Memcheck的檢查機制可歸納如下:
1、儲存器中的每個位元組有兩個屬性:該位元組中的值是否有效和該位元組是否可以訪問;
2、讀取或寫入儲存器時,會檢查地址是否有效,如果是無效的地址,則Memcheck會發出無效讀取或無效寫入錯誤;
3、當儲存器讀入CPU暫存器或從暫存器寫入儲存器時不會檢查值的有效性;
4、當CPU暫存器中的值用於生成儲存器地址或確定條件分支的結果時,將檢查這些值的有效性,如果未定義任何值,則發出錯誤;
5、一旦檢查這些值的有效性後,就將它們設定為檢查過的,即標記為值有效,以後再檢查就認為是有效的,這避免了重複錯誤。
6、從記憶體載入值時,Memcheck會檢查該地址是否有效,並在需要時發出非法地址警告。在這種情況下,儘管地址無效,也會將該值標記為有效的,目的是減少呈現給使用者的混亂資訊量。這樣避免了既地址無效又值無效的現象,準確定位錯誤原因。
7、對於來自部分有效且部分無效的地址的多位元組載入,存在模糊的邊界情況。有關詳細資訊,請參閱選項–partial-loads-ok
8、Memcheck會記錄分析如下函式:malloc、calloc、realloc、valloc、memalign、free、new、 new[]、delete和 delete[]。
8.1、malloc、new、new[]:分配的記憶體被標記為可定址的但不具有有效的值。這意味著初始化後才能使用它們。
8.2、calloc:分配的記憶體標記為可定址和有效,因為calloc將區域清除為零。
8.3、realloc:如果新分配的記憶體大於舊的,則多出的部分標記為地址有效但值無效,如同 malloc。如果新分配的記憶體小於舊的,則失去的部分標記為不可定址(地址無效)。
8.4、free、delete、delete[]:傳遞給這些函式的指標(指向的地址)必須是之前malloc、new、new[]等返回的,否則,Memcheck會報錯。如果指標確實有效,則Memcheck將其指向的整個區域標記為不可定址(釋放後地址無效了),並將該塊放置在freed-blocks-queue中,目的是儘可能延遲重新分配這個區塊。如果在釋放後,再去訪問它就會引發無效地址錯誤。