2016-03-31 70 views
0

我有以下代碼片段。看似無效的內存訪問未被CMBC報告

#include<stdio.h> 
#include<stdlib.h> 
int main() 
{ 
     char *c = malloc(1); 
     printf("%p\n", c); 
     c = c + 20; 

     printf("%p\n", c); 
     printf("%d\n", *c); 

     free(c - 20); 
     return 0; 
} 

在這段代碼中,我將一個字節的內存分配給一個指針。然後,我正在訪問一個分配了內存20個單元的內存位置。當我解除引用指針時,我預計會得到內存訪問衝突錯誤或分段錯誤或類似的東西。我沒有得到任何這樣的錯誤。

讓我們假設這是一個未定義的行爲的情況。所以我試圖用CBMC這個衆所周知的模型檢查器使用下面的命令來驗證這個程序。

cbmc test01.c --pointer-check 

CBMC報告說程序是安全的。這是CBMC的問題還是我錯過了什麼?

+0

看到'g ++'with -'fsanitize = address'說了這個很有趣。我看不出任何方式,這將是「好」;也許這是一個CBMC錯誤? – OMGtechy

+0

如果訪問仍在堆分配內,則不會收到內存訪問衝突。 C沒有用數組索引來握住你的手。 –

+1

'我期望得到一個內存訪問衝突錯誤。我沒有得到那個錯誤。「是什麼讓你覺得'c + 20'有受保護的內存?使用'(void *)c'代替'%p' –

回答

0

正如您在您的問題中所述,陳述printf("%d\n", *c);公開了未定義的行爲。作爲undefined,你可能會反對的任何期望是錯誤的。這包括獲取特定錯誤或任何錯誤。

C運行時庫不會檢查程序訪問內存的方式。如果這樣做,該計劃將運行得非常慢很多。關於堆,C運行時庫執行一些基本檢查;例如,它在程序啓動時在地址0處放置一定的值,並檢查程序結束時該值是否仍然存在。如果值改變了,那麼空指針被寫入時被取消引用,並且它可以警告你。

c = c + 20;之後,c很可能指向屬於您程序的一塊內存。它可以是堆上的一個空閒區域,它可以位於堆管理器用來處理堆的數據結構中,但它仍然存在於相同的內存頁面上的可能性很大。

如果您有(壞)運氣,並且c + 20降落到存儲c的內存頁之外,則會發生由操作系統處理的異常情況。它會終止程序並顯示與您在問題中列出的錯誤消息類似的錯誤消息(思路相同,每個操作系統上的詞語和演示文稿都不相同)。


更新

分配內存某種魔力。該程序以一塊內存(稱爲「堆」)開始,該內存由操作系統分配給程序用於此目的。

C運行時庫包含管理堆的代碼。該代碼使用這部分內存的一小部分用於簿記。一個常見的實現使用雙鏈表,列表中每個節點的有效載荷是程序使用<memory.h>malloc()calloc()等)中聲明的函數「分配」的一塊存儲器。當發生對malloc()的調用時,此代碼將運行,在列表中創建一個新節點並返回節點有效負載的地址(堆內的地址)。

程序根據需要使用此指針。例如,您的程序可以在c-1處自由編寫。實際上,在malloc()裏面實際上是在那裏寫信息的。在malloc()返回c之後,您的代碼也可以在c-1處編寫。從OS的角度來看,這兩個寫操作沒有區別。而且,由於C不是託管或解釋型語言,程序中不包含任何代碼,用於監視您編寫的代碼所做的操作或握住其手寫錯誤位置的內容。

如果您在c-1處編寫,您很可能會破壞堆管理器使用的數據結構。沒有錯誤立即發生。沒有錯誤消息顯示,您的程序繼續運行顯然是正常的。但在下一次調用處理堆的函數時(無論是內存分配還是釋放),程序將開始肆虐。堆數據結構正在被破壞,任何事情都可能發生。


關於CBMC,我不知道它是如何工作的。也許它無法檢測到這種情況。或者,它可能會將您的程序報告爲安全的,因爲它在增量後不會寫入c

+1

預計會出現* undefined *行爲。我的具體問題是爲什麼'CBMC'沒有報告這個問題。 – niyasc

+0

@niyasc你可以按照CMBC頁面上的鏈接,他給出一個電子郵件地址。 –

+0

@niyasc當你期望某些東西時,那東西不再是未定義的。我更新了答案,並簡要說明了堆的工作原理以及爲什麼從'c + 20'讀取不會觸發任何錯誤消息。 – axiac

1

順便說一句。 gcc -fsanitize=address確實編譯沒有警告這個文件,但是當你運行代碼,你會得到從地址的消息sanitzer

================================================================= 
==24198==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000f004 at pc 0x400921 bp 0x7ffe1e66b900 sp 0x7ffe1e66b8f0 
READ of size 1 at 0x60200000f004 thread T0 
    #0 0x400920 in main (/home/ingo/test/c/sanitize_address+0x400920) 
    #1 0x7fdd9ddca7af in __libc_start_main (/lib64/libc.so.6+0x207af) 
    #2 0x4007d8 in _start (/home/ingo/test/c/sanitize_address+0x4007d8) 

0x60200000f004 is located 19 bytes to the right of 1-byte region [0x60200000eff0,0x60200000eff1) 
allocated by thread T0 here: 
    #0 0x7fdd9e19c7b7 in malloc (/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/libasan.so.1+0x577b7) 
    #1 0x4008b7 in main (/home/ingo/test/c/sanitize_address+0x4008b7) 
    #2 0x7fdd9ddca7af in __libc_start_main (/lib64/libc.so.6+0x207af) 

SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 main 
Shadow bytes around the buggy address: 
    0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 01 fa 
=>0x0c047fff9e00:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
    0x0c047fff9e50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
Shadow byte legend (one shadow byte represents 8 application bytes): 
    Addressable:   00 
    Partially addressable: 01 02 03 04 05 06 07 
    Heap left redzone:  fa 
    Heap right redzone:  fb 
    Freed heap region:  fd 
    Stack left redzone:  f1 
    Stack mid redzone:  f2 
    Stack right redzone:  f3 
    Stack partial redzone: f4 
    Stack after return:  f5 
    Stack use after scope: f8 
    Global redzone:   f9 
    Global init order:  f6 
    Poisoned by user:  f7 
    Contiguous container OOB:fc 
    ASan internal:   fe 
==24198==ABORTING 

這樣的輸出可以是相當有幫助找到這種泄漏和溢出。

但是編譯器找到運行期間發生的錯誤並不是一件簡單的事情。