程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

又一年即將結束,是時候盤點一下開源專案中的 Bug 了。2020 年的盤點可能還需要點時間,本文我們先來看看 2019 年開源 C/C++ 專案中遇到的一些最有趣的槽點。

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

No。 10。 我們正執行在什麼作業系統上?

V1040 可能拼寫錯誤預定義宏名稱。’MINGW32_‘有點兒像’MINGW32__’。winapi。h 4112

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

MINGW32_ 宏的名稱拼寫有誤(MINGW32 實際上被宣告為MINGW32__)。在專案的其它地方,拼寫是正確的:

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

順便說一句,這個 bug 並不是在文章“CMake: the Case when the Project’s Quality is Unforgivable”中首次被描述,而是在一個開源專案的 V1040 診斷中就真正被第一次發現的 bug(2019 年 8 月 19 日)。

https://www。viva64。com/en/b/0658/?ref=hackernoon。com

No。 9。 哪個先?

V502 可能’?:‘運算子的工作方式與預期不符。’?:‘運算子的優先順序比’==‘運算子低。mir_parser。cpp 884

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

我們感興趣的下面的部分:

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

’==‘運算子的優先順序比三元運算子 (?:) 高。因此,這個條件表示式的求值順序錯誤,等效於如下程式碼:

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

小編推薦一個學C語言/C++的學習裙【 一零三零 六五二 八四七 】,無論你是大牛還是小白,是想轉行還是想入行都可以來了解,一起進步一起學習!裙內有開發工具,很多幹貨和技術資料分享!

由於常量 OP_intrinsiccall 和 OP_intrinsiccallassigned 都是非 null 的,這個條件會一直返回 true,這意味著 else 分支是無法訪問的程式碼。

這個 bug 在文章“Checking the Ark Compiler Recently Made Open-Source by Huawei”中被提到。

https://www。viva64。com/en/b/0690/?ref=hackernoon。com

No。 8。 危險的位運算子

V1046 在位運算子’&=’中不安全地使用’bool’和’int’型別。GSLMultiRootFinder。h 175

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

程式碼建議 SetFunctionList 函式遍歷一個迭代器列表。如果至少有一個迭代器是無效的,這個函式會返回 false,否則就返回 true。

然而,SetFunctionList 函式對於有效的迭代器也會返回 false。讓我們來看看是為什麼。AddFunction 函式返回 fFunctions 列表中有效迭代器的數目。也就是說,新增非空迭代器將導致列表的大小遞增:1、2、3、4,以此類推。這就是 bug 生效的地方:

ret &= AddFunction(*f);

由於這個函式返回一個 int 型別的值而不是 bool 型別,因此對於偶數值’&=‘運算子也會返回 false,因為偶數的最低有效位始終設定為 0。這就是為什麼一個微小的 bug 會打破 SetFunctionsList 的返回值,即使它的引數是有效的。

如果你仔細閱讀了程式碼片段(你是認真的,對吧?),你可能已經發現,它來自 ROOT 專案。是的,我們也發現了這個 bug:“Analyzing the Code of ROOT, Scientific Data Analysis Framework”。

https://www。viva64。com/en/b/0682/?ref=hackernoon。com

No。 7。 變數混淆

V1001[CWE-563] ’Mode’變數被賦值了,但是直到函式結束都沒有被使用。SIModeRegister。cpp 48

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

對函式引數和類成員使用相同的名字是非常危險的,因為你很可能把它們混淆。而這裡就是這樣的。下面的表示式沒有意義:

Mode&= Mask;

函式的引數變化之後,這個引數之後不會以任何形式被使用。程式設計人員很可能想要寫的是:

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

這個 bug 在 LLVM 發現。我們有一個傳統,不時地檢查這個專案。今年我們又 檢查了一次這個專案。

No。 6。 C++ 有自己的的規則

這個 bug 源於 C++ 規則並不總是遵循數學規則或“常識”。看看下面的程式碼片段,試著自己找出 bug 吧。

V709 發現的可疑比較:‘f0 == f1 == m_fractureBodies。size()’。記住, ‘a == b == c’並不等價於’a == b && b == c’。

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

這個條件表示式似乎是在檢查 f0 等於 f1,並且等於 m_fractureBodies 中元素的數目。這可能意味著,檢查 f0 和 f1 是否位於 m_fractureBodies 陣列的末尾,因為它們都包含被 findLinearSearch() 方法發現的一個物件。但實際上,這個條件表示式檢查 f0 是否等於 f1,然後檢查 m_fractureBodies。size() 是否等於 f0 == f1 表示式的結果。也就是說,這裡第三個運算數是 0 或 1。

這是一個好 bug!而且,幸運的是,這個 bug 非常罕見。到目前為止,我們只在 3 個開源專案中看到過這個 bug,而且有趣的是,這 3 個專案都是遊戲引擎。這不是 Bullet 中發現的唯一 bug;最有趣的一些 bug 在文章“PVS-Studio Looked into the Red Dead Redemption’s Bullet Engine”有描述。

https://www。viva64。com/en/b/0647/?ref=hackernoon。com

No。 5。 行末尾是什麼?

這個 bug 很容易發現,如果你知道其中細節的話。

V739 EOF 不應該與一個’char’型別的值進行比較。‘ch’應該是’int’型別。json。cpp 762

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

這是你很難發現的一些 bugs 之一,如果你不知道 EOF 是被定義為 -1 的話。因此,如果你試圖將它與一個帶標誌的字元型別變數比較時,條件表示式的結果幾乎總會是 false。唯一的例外是編碼為 0xFF(255) 的字元。當與 EOF 比較時,這個字元會變成 -1,因此會讓這個條件表示式的結果為 true。

在這幾年的眾多 bugs 中,前 10 名都是在計算機遊戲軟體中發現的:引擎或開源遊戲。你可能已經猜到了,這個 bug 也是來自遊戲領域。更多錯誤在“Cataclysm Dark Days Ahead: Static Analysis and Roguelike Games”一文中有描述。

https://www。viva64。com/en/b/0628/?ref=hackernoon。com

No。 4。 常量 Pi

V624 對於’3。141592538’常量可能有錯誤列印。考慮使用 中的 M_PI 常量。PhysicsClientC_API。cpp 4109

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

在 Pi 數字 (3,141592653…) 中有一個微小的列印錯誤:第 7 個小數位的數字“6”丟失了。

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

一個不正確的百萬分之一的小數位很難造成任何明顯的損害,但是最好使用庫裡已有的常量,其正確性有所保障。例如,Pi 數字由標頭檔案 math。h 中的 M_PI 常量表示。

你可能在文章“PVS-Studio Looked into the Red Dead Redemption’s Bullet Engine”中已經讀到過這個 bug,它在其中排第 6 位。如果你還沒讀到過,這次可別錯過。

https://www。viva64。com/en/b/0647/?ref=hackernoon。com

No。 3。 難以捉摸的異常

V702std::exception(以及類似的)中的類應該是’public’的(沒有指定關鍵字的話,編譯器預設是’private’的)。CalcManager CalcException。h 4

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

分析器檢測到來自 std::exception 的一個類使用了 private 修飾符(如果沒有指定的話預設使用 private)。這段程式碼的問題是,試圖捕獲一個通用的 std::exception 將會導致這個程式錯過 CalcException 型別的異常。這個行為源於 private 繼承禁止隱式型別轉換。

你肯定不希望看到你的程式因為一個漏掉的 public 修飾符而崩潰。順便說一句,我打賭你在一生中肯定至少用過這個應用程式一次,因為它就是老的 Windows Calculator,我們早幾年也檢查過這個應用程式。

No。 2。 未閉合的 HTML 標籤

V735 可能是一個不正確的 HTML。碰到““閉合標籤時,預期的是”” 標籤。book。cpp 127

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

由於它經常發生,C/C++ 原始碼本身沒有太多說明,因此讓我們看看上面的程式碼片段生成的預處理程式碼:

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

分析器發現了一個未閉合的 div 標籤。這裡有很多 html 程式碼片段,因此作者需要修改程式碼。

很驚訝我們能診斷出這種型別的 bugs 嗎?我第一次看到這一點時,印象也非常深刻。因此,是的,我們確實知道一些關於分析 html 程式碼的知識。不過,只在 C++ 程式碼中才行。:)

不僅這個 bug 被排在第二位,這也是我們的前 10 榜單中的第二個計算器。可以閱讀“Following in the Footsteps of Calculators: SpeedCrunch”這篇文章,來看看我們在這個專案發現的其它 bugs。

https://www。viva64。com/en/b/0618/?ref=hackernoon。com

No。 1。 難以捉摸的標準函式

這個 bug 位於第一位,是一種非常奇怪的 bug,能夠成功透過程式碼評審。

你自己試試發現這個 bug:

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

現在讓我們看看分析器怎麼說:

V560 部分條件表示式總是 true:(’\n’ != c)。params。c 136。

很奇怪,對不對?讓我們看看在另一個檔案(charset。h)中其它奇怪的點:

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

嗯,這確實很奇怪… 因此,如果變數 c 等於’\n’,那麼看起來無害的函式 isspace© 會返回 false,從而因為短路邏輯而不執行第二部分的檢查。而且如果 isspace© 執行,變數 c 將會等於 ‘’ 或’\t’,明顯不會等於’\n’。

你可能會說,這個宏與 #define true false 類似,像這樣的程式碼永遠不能透過一個程式碼評審。但是這個特殊的程式碼片段確實通過了程式碼評審——而且還在程式碼庫中等待被發現。

有關這個 bug 的更詳細的評論,請檢視文章“Wanna Play a Detective? Find the Bug in a Function from Midnight Commander”。

https://www。viva64。com/en/b/0610/

結 論

程式人生:讀開源專案需謹慎!盤點C++開源專案中的十大BUG

我們發現的這些 Bug 都是一些常見的複製 - 貼上錯誤、不準確的常量、未閉合的標籤以及許多其它缺陷。但是我們的分析器正在不斷演進和 學習 來診斷越來越多型別的問題,因此我們肯定不會放慢腳步,並且會像以前一樣定期釋出關於專案中發現的 bugs 的新文章。

作者介紹:

PVS-Studio 致力於尋找 C、C++、C#、Java 在 Windows、Linux 和 macOS 上的 bugs。

頂部