透過AddressSanitizer來發現實際產品的Bug

ASan,你值得擁有

感謝Aaron Gorenstein授權釋出這篇文章。

從最近釋出的Visual Studio 2019 v16。9開始,用於MSVC的ASan(AddressSanitizer)元件就正式可以使用了。 我們已經展示瞭如何透過它尋找程式碼中的Bug,甚至在產品級別的程式碼(例如EASTL),我們也可以用上它。今天的這篇文章中,我們將會展示我們是如何使用ASan找到了MSVC編譯器內部的一個Bug的。

想法很簡單:ASan很善於查詢程式碼中的Bug,而且我們也總是對編譯器中的Bug十分感興趣。就像你可以在你的工程中啟用ASan並執行測試一樣,我們在我們的MSVC編譯器專案上也啟用了ASan,測試發現,還真的找到了一個Bug。

啟用ASan並編譯程式碼

在我們的構建系統中啟用ASan非常容易。在通用構建場景中,我們已經文件化了如何啟用ASan的具體步驟。在我們的案例中,我在構建指令cl。exe上添加了/fsanitize=address開關,另外,我們舊的但在不斷演進的構建系統還需要一些額外的手動配置來指定我們需要的擴充套件庫的位置資訊。

這樣就可以了。現在,我可以構建二進位制檔案了,也就是c2。dll,就和之前一樣。但是,在生成的二進位制檔案內部,有大量的ASan監控程式碼被插入到了二進位制程式碼中,以查詢那些潛在的Bug。接下來,我就可以和以前一樣執行一系列的內部測試流程,看看會不會有任何問題。

找到Bug

我們的測試套件中大概有4000個單獨的C++檔案,包含了真實世界的程式碼,手工測試程式碼,效能指標測試以及迴歸測試。我們有一個內部開發的只能從命令列訪問的測試程式。當執行它時,我們幾乎通過了所有的測試專案,但是有一個出現了失敗,透過檢視日誌,我發現如下的診斷資訊:

具體來說,我有如下的發現:

1) 被報告的錯誤是”stack-buffer-underflow”:ASan可以找到有關棧和堆方面的問題。

2) 請注意這一行”stack of thread T3″。在c2。dll中,我們會並行執行很多執行緒,例如有T1,T2等。ASan可以處理這種多執行緒的程式碼場景。

最重要的是:ASan從來沒有誤報的情況。這個錯誤報告確實是我們程式碼裡的一個Bug,因此我已經知道修復它的方式了。

幸運的是,觸發此錯誤的輸入是一個單個檔案。我可以簡單地重複命令列執行來重現這個Bug。下圖展示了我是如何觸發這個Bug的:

我去除了一些輸出,但是在上圖中,我們還是可以看到ASan的命令列診斷資訊。我可以利用這個資訊(上圖中的堆疊)來分析這個Bug。但我個人還是喜歡在完整的IDE中來分析和除錯問題。透過下面這個命令列,我可以重現這個ASan問題,同時將它附加在偵錯程式中。

啟動附加二進位制檔案之後的偵錯程式,我看到了如下的景象:

IDE可以提供關於這個Bug的更加豐富的資訊。你可以看到,這個ASan問題被報告為一個異常,並指明瞭具體出現在程式碼的哪一行,另外還有我熟悉的除錯堆疊,以及其他的資訊。另外,在輸出視窗我們也可以看到一些很有幫助的資訊。

大家可以猜到Bug藏在哪裡嗎?

提示:”sz”看起來是代表一個”size”。然後,再回憶一下Asan的報告:”stack buffer underflow”。

修復Bug

透過檢查sz變數,我們可以更加清楚的明白問題的原因:MscIsFloatOrVectorConstant函式如果找到則返回內容長度,否則返回0。這個Bug場景中,它返回了0,我們在函式的區域性結構體vval中發生了下溢位。修復方法也比較直接:我們在第16828行添加了一個判斷程式碼。這個修復已經被整合到正式程式碼中,並將包含在即將釋出的v16。10中。

這個特定的錯誤不太可能在真實世界出現:堆疊將需要以正確的方式儲存一些垃圾值(以透過第16831行的條件)。但是,從理論上講,此錯誤(更廣泛地說,就是像它一樣的錯誤)可能會導致程式碼中的最佳化不正確。 那是編譯器可能遇到的最嚴重的錯誤之一:silent-bad-codegen。 我很高興能找到這個錯誤,我也很高興能夠與你分享ASan可以很容易地修正此類錯誤。

結論

我們通常不會寫有關修復編譯器中的錯誤的部落格文章,但我們想表達的是,ASan能幫助你輕鬆有效地發現並修復錯誤:

> 我們定製的命令列驅動構建系統只需幾行更改即可整合build-with-ASan選項。

> 構建完成後,就可以無縫測試二進位制檔案:我運行了典型的內部開發迴圈測試套件。

> 一旦發現問題,就可以無縫重複我們IDE偵錯程式中的步驟,將我直接指向要檢查的原始碼行。

> 確切的原始碼行,再加上ASan能夠描述問題的能力(堆疊下溢),使調查迅速而輕鬆。不需要很長的時間的分析:當然,我仍然必須確認並實際解決該問題,但是與傳統的錯誤修復程式相比,Bug分析的大部分工作都是很快速的。

我希望ASan的高效率,有效性和簡單性可以打動正在閱讀本文的你。而且,對我來說,最引人注目的是:ASan發現了一個記憶體衝突,而且此衝突尚未在我們的程式中表現出不良的行為。它可以體現出來,但是在這裡,我們無需花費更多,更間接的程式碼分析就能確定並解決它,並且希望它不會影響到我們的客戶,這是太Biang了。

那就,試試看唄!

最後

Microsoft Visual C++團隊的部落格是我非常喜歡的部落格之一,裡面有很多關於Visual C++的知識和最新開發進展。大浪淘沙,如果你對Visual C++這門古老的技術還是那麼感興趣,則可以經常去他們那(或者我這)逛逛。

本文來自:《Finding Bugs with AddressSanitizer: MSVC Compiler》

相關文章