使用 Infer 進行靜態程式碼分析
一、簡介
在軟體開發領域,確保程式碼品質非常重要,尤其是對於複雜且大型的程式碼庫。像Infer這樣的靜態程式碼分析工具為我們提供了在程式碼庫中潛在的錯誤和漏洞顯現為嚴重問題之前檢測它們的技術。
在本教程中,我們將探索程式碼分析的基礎知識,探索 Infer 的功能,並提供將其合併到我們的開發工作流程中的實用見解。
2. 靜態程式碼分析
靜態分析是一種在不執行程式的情況下自動檢查原始程式碼的偵錯方法。此流程有助於識別潛在缺陷、安全漏洞和可維護性問題。靜態程式碼分析通常由第三方工具(例如著名的SonarQube)進行,自動化後靜態程式碼分析非常簡單。
一般來說,它發生在早期開發階段。編寫程式碼後,應該運行靜態程式碼分析器來檢查程式碼。它將根據標準或自訂預定義規則檢查定義的編碼規則。一旦程式碼通過靜態程式碼分析器運行,分析器就會識別出程式碼是否符合設定的規則。
在基本的企業環境中,它通常是持續整合(CI)流程的一部分。每次提交都會觸發一個作業來建立應用程式、執行測試和分析程式碼,從而在部署之前確保合規性、安全性和安全性。
3. 推斷
Infer 是用於 Java、C、C++ 和 Objective-C 的靜態程式碼分析工具,以OCaml編寫。它最初由 Facebook 開發,並於2015 年開源。從那時起,它的受歡迎程度超越了 Facebook,並被其他大公司採用。
對於 Java 和 Android 程式碼,它會檢查空指標異常、資源外洩和並發競爭條件等問題。在 C、C++ 和 Objective-C 中,它可以偵測空指標問題、記憶體洩漏、編碼約定違規以及呼叫不可用的 API 等問題。
為了有效地處理大型程式碼庫,Infer 利用了分離邏輯和雙向溯因等技術。分離邏輯允許它獨立分析程式碼儲存的小部分,避免一次處理整個記憶體空間的需要。 Bi-abduction 是一種邏輯推理方法,可以幫助Infer 發現程式碼不同部分的屬性,使其在後續分析時只專注於修改的部分。
透過結合這些方法,該工具可以在短時間內發現對由數百萬行程式碼建構的應用程式進行修改時出現的複雜問題。
3.1.推斷階段
無論輸入語言如何,Infer 都分兩個主要階段運行:捕獲階段和分析階段。
在捕獲階段,Infer 會捕獲編譯命令以將要分析的檔案翻譯為其內部中間語言。這種翻譯與編譯類似,因此 Infer 從編譯過程中取得資訊來執行自己的翻譯。此外,這也是我們使用編譯指令呼叫 Infer 的原因,例如:
infer run -- javac File.java
因此,檔案會像往常一樣被編譯,Infer 也會將它們翻譯成第二階段進行分析。 Infer 將中間檔案儲存在結果目錄中, infer-out/by
並在呼叫 infer 命令的資料夾中建立。
此外,我們可以使用capture
子命令僅呼叫捕獲階段:
infer capture -- javac File.java
在分析階段, infer-out/
中的檔案由Infer單獨分析。每個函數和方法分別進行分析。如果 Infer 在分析方法或函數期間遇到錯誤,它會停止對該特定實體的分析,但會繼續分析其他實體。因此,典型的工作流程包括在程式碼上執行 Infer、解決已識別的錯誤以及重新執行 Infer 以發現其他問題或確認修復。
偵測到的錯誤將在標準輸出和名為infer-out/report.txt
的檔案中報告。推斷過濾器並突出顯示最有可能真實的錯誤。
我們可以使用analyze
子命令僅呼叫分析階段:
infer analyze
3.2.推論全局和差異工作流程
預設情況下,Infer 將刪除先前的infer-out/
目錄(如果存在)。這形成了一個全局工作流程,每次都會分析整個專案。
將–reactive
或-r
傳遞給 Infer 可防止刪除infer-out/
目錄,從而導致不同的工作流程。例如,行動應用程式使用增量建置系統,其中程式碼隨著一系列程式碼變更而演變。對於這些變更,僅分析專案目前的變更是有意義的,而不是每次分析整個專案。因此,我們可以利用 Infer 的反應模式並切換到差異化工作流程。
3.3.分析專案
要使用 Infer 分析文件,我們可以使用javac
和clang
等編譯器。另外,我們可以使用gcc
,儘管 Infer 將在內部使用clang
。此外,我們可以將 Infer 與各種建置系統一起使用。
一種流行的 Java 建置系統是 Maven,我們可以透過以下方式與 Infer 一起使用:
infer run -- mvn <maven target>
另外,我們可以將 Infer 與 Gradle 一起使用:
infer run -- gradle <gradle task>
3.4. CI 的推薦流程
Infer 建議使用差異化工作流程進行持續整合 (CI)。因此,流程將是確定修改的檔案並以反應模式執行分析。另外,如果我們想運行多個分析器,則將捕獲階段分開會更有效,以便所有分析器都可以使用結果。
4. 運行推斷
我們有多種使用 Infer 的選項:二進位版本、從原始碼建置 Infer 或 Docker 映像。 Infer 入門頁面解釋了我們如何取得並運行 Infer。
現在,我們可以建立一些虛擬 Java 程式碼片段並分析它們。並不是說我們只會介紹 Infer 可以識別的少數問題,可以在此處找到 Infer 可以檢測到的所有類型問題的完整清單。
4.1.空取消引用
public class NullPointerDereference {
public static void main(String[] args) {
NullPointerDereference.nullPointerDereference();
}
private static void nullPointerDereference() {
String str = null;
int length = str.length();
}
}
如果我們根據這段程式碼進行推斷,我們將得到以下輸出:
Analyzed 1 file
Found 1 issue
./NullPointerDereference.java:11: error: NULL_DEREFERENCE
object str last assigned on line 10 could be null and is dereferenced at line 11
9. private static void nullPointerDereference() {
10. String str = null;
11. > int length = str.length();
12. }
13. }
24.
Summary of the reports
NULL_DEREFERENCE: 1
4.2.資源洩漏
public class ResourceLeak {
public static void main(String[] args) throws IOException {
ResourceLeak.resourceLeak();
}
private static void resourceLeak() throws IOException {
FileOutputStream stream;
try {
File file = new File("randomName.txt");
stream = new FileOutputStream(file);
} catch (IOException e) {
return;
}
stream.write(0);
}
}
現在,透過執行 Infer,我們可以看到偵測到資源洩漏:
Analyzed 1 file
Found 1 issue
./ResourceLeak.java:21: error: RESOURCE_LEAK
resource of type java.io.FileOutputStream acquired to stream by call to FileOutputStream(...) at line 17 is not released after line 21
19. return;
20. }
21. > stream.write(0);
22. }
23. }
24.
Summary of the reports
RESOURCE_LEAK: 1
4.3.被零除
public class DivideByZero {
public static void main(String[] args) {
DivideByZero.divideByZero();
}
private static void divideByZero() {
int dividend = 5;
int divisor = 0;
int result = dividend / divisor;
}
}
我們的程式碼顯示以下輸出:
Analyzed 1 file
Found 1 issue
./DivideByZero.java:9: error: DIVIDE_BY_ZERO
The denominator for division is zero, which triggers an Arithmetic exception.
6. private static void divideByZero() {
7. int dividend = 5;
8. int divisor = 0;
9. > int result = dividend / divisor;
10. }
Summary of the reports
DIVIDE_BY_ZERO: 1
5. 結論
正如我們在本教程中所發現的,靜態程式碼分析工具對於軟體開發過程至關重要。 Infer 就是這樣的工具之一,它因其能夠檢測各種程式語言中的各種問題而脫穎而出。透過利用 Infer 的靜態程式碼分析,我們可以主動解決錯誤和漏洞,從而開發出更可靠、更安全的軟體應用程式。
與往常一樣,原始碼可以在 GitHub 上取得。