一個 bug 通常不是獨一無二的#

一個地方的錯誤很可能在其他地方也存在,原因包括:

  • 開發者以同樣的方式犯錯
  • 某個容易被誤用的 API
  • 有問題的程式碼被複製貼上到其他地方

在成熟的開發文化和安全關鍵的工作中,修復一個 defect 後不會就此停止。目標是修復整個類別的 defect,並確保類似的問題不會在未來再次出現。

搜尋同類型的問題#

例如你修復了一個除以零的問題:

double a = getWeight(subNode) / totalWeight;

接下來應該搜尋所有其他除以 totalWeight 的地方:

grep -r '/ *totalWeight' .

然後考慮是否有其他除法也可能失敗。一個 Unix pipeline 可以幫助你在四百萬行 C 程式碼中快速篩選:

# Find divisions, assuming spaces around the / operator
grep -r ' / ' . |
# Eliminate those involving sizeof
grep -v '/ sizeof' |
# Color divisors for easy inspection and
# eliminate divisions involving numerical or symbolic constants
grep --color=always ' / [^0-9A-Z][^,;)]*' |
# Remove duplicates
sort -u

這些篩選器將嫌疑行數從 5,731 縮減到 5,045,再到 2,032,最後到 1,923——一個可以在合理時間內人工檢查的數量。雖然篩選器不是百分百準確(sizeof 可以回傳零、symbolic constant 可以為零),但這比聲稱「檢查所有除法太費工」然後什麼都不做要好得多。

預防未來的同類問題#

考慮採取措施防止類似 fault 再次出現:

  • 隱藏危險 API 並提供安全替代
#define gets(x) USE_FGETS_RATHER_THAN_GETS(x)

使用 gets(以 buffer overflow 聞名)的程式將無法編譯或連結。

  • 如果 fault 源自不正確的型別處理,引入更嚴格的型別檢查
  • 加入 static analysis 或加強其設定來自動偵測類似問題

重點回顧#

  • 修復一個 fault 後,找出並修復類似的 fault,並採取措施確保它們不會在未來再次出現