事后諸葛亮:如何寫(xiě)出沒(méi)有bug的軟件
網(wǎng)上對(duì)蘋(píng)果iOS7操作系統(tǒng)中 ***暴露出的一個(gè)嚴(yán)重安全漏洞的討論讀起來(lái)十分有趣。如果你還沒(méi)有讀過(guò) Alex Langley對(duì)此的分析,那現(xiàn)應(yīng)該讀一下,寫(xiě)的非常好。
附帶說(shuō)一下,是一個(gè)TLS v1.2 SSL連接問(wèn)題上的bug,簽名認(rèn)證沒(méi)有被檢查,使偽造簽名成為可能。原因是代碼***的直接跳到了方法的結(jié)尾處,沒(méi)有實(shí)際檢查哈希結(jié)果是否正確。
是什么導(dǎo)致了這樣一個(gè)弱者的bug?我四處看了一下,下面是網(wǎng)友們總結(jié)出的幾個(gè)原因:
- 用C語(yǔ)言很難寫(xiě)出正確無(wú)誤的程序
- 蘋(píng)果公司的程序員不用心
- 編碼風(fēng)格中允許忽略大括號(hào)
- 蘋(píng)果公司里沒(méi)有正規(guī)的代碼審查
- 使用了goto語(yǔ)句
- 使用了自動(dòng)代碼合并
- 沒(méi)有開(kāi)啟編譯器對(duì)死代碼的警告
- 沒(méi)有使用靜態(tài)分析工具
- 沒(méi)有好好測(cè)試
所有的這些原因看起來(lái)都像是在這個(gè)bug的產(chǎn)生中扮演了一定的角色。但有一個(gè)主導(dǎo)原因嗎?
對(duì)代碼的差異對(duì)比給不了多少有用的信息,在631行上的這個(gè)bug看起來(lái)怎么產(chǎn)生的都有可能。也許是一次代碼合并錯(cuò)誤,也許是愚蠢的拷貝/粘貼造成。
事實(shí)上,你很難,或許不可能找到一個(gè)單一的為此bug負(fù)責(zé)的原因,那我們能有什么良方?很多人說(shuō)是指代碼上沒(méi)有用花括號(hào)包圍的原因。例如Zed說(shuō):
很顯然寫(xiě)這段Apple SSL C 代碼的家伙沒(méi)有讀過(guò)我的C語(yǔ)言書(shū):永遠(yuǎn)使用花括號(hào)!
— zedshaw (@zedshaw) February 23, 2014
這就是帕金森瑣碎定理的一個(gè)很好的例子:花費(fèi)大量時(shí)間討論無(wú)關(guān)緊要的瑣事。引起這個(gè)bug的根源不是缺少花括號(hào)。有沒(méi)有花括號(hào)不會(huì)成為這個(gè)多余的goto的產(chǎn)生的原因。
為什么我們,程序員們,總是抱怨編碼風(fēng)格問(wèn)題,但卻不重視代碼審查對(duì)程序正確性的決定作用呢?雖然不好的編碼風(fēng)格會(huì)隱藏程序bug,但并不是編碼 風(fēng)格產(chǎn)生的問(wèn)題。我們太重視代碼布局視覺(jué)上的問(wèn)題,卻故意逃避正確性問(wèn)題。如果更注重正確性,絕對(duì)不可能讓這種關(guān)鍵代碼中未經(jīng)測(cè)試的情況下發(fā)布。
好的編碼風(fēng)格并不能防止大部分的bug的產(chǎn)生——盡管有點(diǎn)作用。簡(jiǎn)單的編程語(yǔ)言能夠減少bug的產(chǎn)生。代碼審查的作用更大。靜態(tài)分析能讓你避免大量的bug。
認(rèn)真的測(cè)試可以捕捉并防止很多bug的產(chǎn)生。這就是為什么對(duì)于關(guān)鍵的軟件,比如cryptography library,有100%的測(cè)試覆蓋率。這種一眼就能看出來(lái)的bug絕對(duì)不會(huì)在這種軟件里出現(xiàn)。
未經(jīng)測(cè)試的加密代碼是用來(lái)解密的代碼。
所以說(shuō),抱怨花括號(hào)是愚蠢的做法。相反,在這種情況下花括號(hào)會(huì)讓問(wèn)題更難發(fā)現(xiàn),花括號(hào)不是問(wèn)題的根源,也不是問(wèn)題的解決方案。大家找錯(cuò)了方向。