2011-07-02 44 views
6

所以我進行了一些靜態代碼分析儀在一些C代碼,有一兩件事令我驚訝的是大約警告:的scanf%d段錯誤,在大的輸入

int val; 
scanf("%d", &val); 

其表示,對於足夠大的輸入可能導致段錯誤。當然,這可以發生。現在修復很簡單(指定一些寬度;畢竟我們知道有多少個有效整數可能最多取決於體系結構),但我想知道的是爲什麼這首先發生,爲什麼這不是在libc中沒有被視爲一個bug(並且是一個簡單的解決方法)?

現在我假設這種行爲有一些原因,首先我錯過了?

編輯:好吧,因爲這個問題似乎沒有這麼明確,更多的解釋: 沒有代碼分析器不會警告scanf一般,但有關scanf讀取沒有指定寬度的數字。

所以這裏有一個最小的工作示例:

#include <stdlib.h> 
#include <stdio.h> 

int main() { 
    int val; 
    scanf("%d", &val); 
    printf("Number not large enough.\n"); 
    return 0; 
} 

我們可以通過發送一個巨大的數量得到了段錯誤(例如,使用的Python):

import subprocess 
cmd = "./test" 
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, shell=True) 
p.communicate("9"*50000000000000) 
# program will segfault, if not make number larger 
+1

你的靜態代碼分析器偷看了'scanf'格式字符串還是隻是盲目地抱怨'scanf'? –

+0

你使用了哪個程序? – ShinTakezou

回答

1

在處理一個整數的第一步是隔離數字的序列。如果該序列比預期的更長,它可能會溢出固定長度的緩衝區,從而導致分段錯誤。

你可以用雙打獲得類似的效果。推到極端,你可以寫1,然後是1000,然後指數是-1000(淨值是1)。實際上,幾年前當我測試這個時,Solaris以穩定的方式處理了1000個數字;它有點超過1024,它遇到了麻煩。

所以,QoI的一個要素就是質量的實現。還有一個要遵循C標準的要素,scanf()在遇到非數字之前不能停止閱讀。這些是相互矛盾的目標。

+2

,因爲它試圖讀取所有的輸入到一個緩衝區的大小是有限的,並沒有檢查緩衝區溢出(也許會更快)?並且不應該這樣執行,因爲格式爲%d,一旦達到可能溢出格式的值,就會停止填充緩衝區,儘管它繼續消耗數字以符合標準? – ShinTakezou

+2

IIUC,'scanf'只需返回超出範圍輸入的任何內容即可。 Glibc實際返回INT_MAX。所以,你可以擁有一個足夠大的緩衝區,用於較大的範圍內輸入並丟棄每個後面的數字。 – ninjalj

+0

是的,據我所知ninjalj是正確的 - 溢出行爲在C中是未定義的,所以scanf可以在這種情況下返回任何想要的(並且可能這就是爲什麼段錯誤也是這樣?)。但是我發現這種行爲非常奇怪 - 沒有理由將整個字符串保存在緩衝區中,畢竟只是讀取它。 – Voo

2

編輯,因爲我錯過了其實你養活一個靜態代碼分析儀,它

如果格式%d配襯的int大小,什麼溢出不應該是什麼寫入VAL通過指針,因爲它應該始終是一個int。嘗試將指針傳遞給long int並查看分析儀是否仍然發出警告。嘗試將%d更改爲%ld,並保留long int指針,然後查看是否再次發出警告。

我想標準應該說一些關於%d,它需要的類型。也許分析器擔心的事實是,在某些系統上int可能比%d的意思短?這聽起來很奇怪。


運行用gcc編譯的例子(我有python 2.6。6)我得到

Traceback (most recent call last): 
    File "./feed.py", line 4, in <module> 
    p.communicate("9"*50000000000000) 
OverflowError: cannot fit 'long' into an index-sized integer 
Number not large enough. 

然後我試圖運行此代替:

perl -e 'print "1"x6000000000000000;' |./test 

和修飾的C部分寫

printf("%d Number not large enough.\n", val); 

我獲得作爲輸出

5513204 Number not large enough. 

其中數字在每次運行改變了,從來沒有出現段錯誤...的GNU scanf函數實現安全......儘管得到的數字是錯的...

+0

雖然可能是這種情況(儘管afaik標準將%d定義爲整數大小),分析儀仍警告不同的問題。我希望增加的例子更清楚。 – Voo

+0

在我看來,它一般抱怨scanf是一種內在的「危險」功能(因爲它會「獲取」)(但問題取決於實現)。正如另一條評論所說,scanf不應該試圖用輸入來填充一個固定大小的內部緩衝區,而不會檢查它是否會溢出!相反,它應該停止嘗試轉換大於INT_MAX的數字並存儲INT_MAX(並使用其餘輸入直到非數字),或使用EOVERFLOW錯誤(但是這是POSIX.1)或ERANGE(C99)或者其他什麼,然後退出。 – ShinTakezou

+0

有趣的是,我在glibc 2.9上運行了一些較老的(古老的,我害怕;不是我的選擇)系統。剛剛在我平常的開發環境和cygwin中嘗試過,它們給出了和你一樣的錯誤。所以看起來這確實是glibc中的一個bug,但是固定在更新的版本上。讓我無需再次檢查最新版本。令人驚訝的是,這樣的錯誤存在了這麼長時間,但至少我不必爲任何現代系統擔心。 – Voo