本文件說明:
- 如何在 Zircon 中使用 Clang Static Analyzer (CSA) 設定跨翻譯單位分析 (CTU);
- Kareem Khazem 在實習期間所做的作品;和
- 其餘必要工作,才能讓 Zircon 全面支援 CTU。
在 Zircon 上設定及執行 CTU
摘要:下載 Clang 原始碼,並在編譯前套用幾個非主修補程式修補程式。在分析工具中執行包裝函式指令碼。下載 CodeChecker
工具;以摘要分析結果,並啟動網路伺服器,透過網頁介面檢視結果。
啟用 CTU 的修補程式
請注意下列兩個修補程式集:
- Samsung 修補程式集是為 Clang 新增 AST 合併支援的一大修補程式。主要內容為
lib/AST/ASTImporter.cpp
的新增內容。tools/xtu-build/*
中也提供一系列 CTU 分析工具 (原始、無法運作效果)。這個修補程式集是以 Clang 的舊版本為基礎。大大小小,就很難將批發到樹狀結構的小端 (ToT) Clang。 - Ericsson 修補程式集,包括一部分 Samsung 的 AST 合併工作,也新增了一些方便進行 CTU 分析的新工具 (
tools/xtu-build-new/*
和tools/scan-build-py/*
)。xtu-build-build 的新工具經過改良,與 Samsung 的 xtu-build 工具略有不同。這個修補程式集比 Samsung One 還新,作者已努力以 ToT 為基礎持續更新這個修補程式。
我們將使用 Ericsson 的修補程式集來修補 Clang,因為 AST 能夠徹底地合併工作,我們也取得了新的分析工具。但請注意,CTU 對 Zircon 的支援不完整。在某些情況下,Samsung 修補程式集即包含提供必要功能的程式碼 (詳情請見下方說明)。
建立支援 CTU 的 CSA 的步驟
- 照常下載及建構 Clang 和 LLVM。
- 在獨立目錄中,複製 Ericsson 的 Clang 分支,然後切換至 ctu-master 分支版本。
下載這個指令碼至 Ericsson 的分支中並執行。它應將一系列修補程式轉儲到修補程式目錄中。我刻意只從 Ericsson 改變初期,到 1bb3636 (這是我實習生的最新修訂版本) 發送修訂版本。
- 如要取得 Ericsson 最新變更的內容,可以嘗試將指令碼中的 1bb3636 改成 HEAD。在指令碼中指定其他範圍,務必略過將上游修訂版本合併至 ctu-master 分支版本的修訂版本。git 記錄 -- 圖表可用於判斷上游修訂版本與 Ericsson 的修訂版本內容
git log --graph --decorate --date=relative --format=format:'%C(green)%h%C(yellow) %s%C(reset)%w(0,6,6)%C(bold green)\n%C(cyan)%G? %C(bold red)%aN%C(reset) %cr%C(reset)%w(0,0,0)\n%-D\n%C(reset)' --all
將產生的修補程式逐一套用至上游 Clang (而非 Ericsson Fork)。
for p in $(ls $PATCH_DIR/*.patch | sort -n); do git am < $p; done
套用 Kareem Khazem 尚未登陸的下文修補程式
重建上游 Clang 與。
執行 CTU 分析
摘要:執行包裝函式指令碼。這樣做會正常建構 Zircon,然後再次建構,但是傾印序列化 AST,而非物件檔案,最後再使用傾印的 AST 分析每個檔案以達到 CTU。
CTU 的運作方式
首先,故事回溯:
非 CTU 靜態分析會分析每個 TU 的 AST;任何向外部函式發出的函式呼叫都會視為不透明。大致來說,CTU 分析是為了該函式實作的 AST,「替換」不透明函式呼叫節點。
因此,CTU 分析會照常開始分析 AST,但在遇到函式呼叫節點時,就會嘗試「合併」該函式的 AST。這會仰賴已事先將資料序列化到磁碟的 AST,好讓分析器可將 AST 重新載入至記憶體。此外,這也依賴於 AST 合併相關支援,這是 ASTImporter.cpp
的 Samsung 修補程式 (以及衍生自 Ericsson 的修補程式)。
為了將 AST 序列化到磁碟,我們必須模擬實際的建構程序。做法是在記錄編譯器叫用時實際執行 Zircon 版本。這可以讓我們「播放」叫用,但編譯器旗標經過修改後,就能轉儲 AST 檔案,而不是物件檔案。
總而言之,這次請快轉:
- 使用 Clang 建構 Zircon,並將建構程序納入 bear 等程式中,以便記錄編譯器叫用並產生 JSON 編譯資料庫。
- 重播相同的編譯步驟,但改為傾印 AST 檔案,而非物件檔案。這就是 xtu-build.py 工具的用途。
- 照常執行靜態分析,但在需要時,將每個已呼叫函式的 AST 還原序列化。透過 Ericsson 團隊編寫的精簡掃描建構替換功能,叫用 scan-build-py/libscanbuild 目錄中的工具,即為 xtu-analyze.py 工具在頂層執行的作業。
相關步驟會在下文所述的 Fuchsia 包裝函式中記錄。產生報告後,系統會提供報告 (格式為 Apple plist) 的完整報表,其中包含已回報錯誤的詳細資料。
Ericsson 的包裝函式指令碼
有兩種工具可用於跨翻譯單位分析:
tools/xtu-build-new
底下的工具是頂層指令碼。由於基礎分析工具可能會失敗 (即因 CSA 異常終止),我已修補xtu-analyze.py
(在 Ericsson 的分支版本中),以便將分析工具 (stdout/stderr,而非報告) 的輸出內容傾印至檔案。輸出內容會根據分析工具的傳回代碼在$OUT_DIR/{passes,fails}
中,其中$OUT_DIR
是傳遞至xtu-analyze.py
的-o
引數的目錄。這些檔案最實用的部分是第二行以analyze: DEBUG: exec command in
開頭,由libscanbuild
工具 (下一個項目符號點) 發出。這個指令是在長時間的繁瑣的指令列修改程序後,實際叫用 CSA。因此,若要使用 gdb 對有問題的檔案執行 CSA,必須使用這個指令。tools/scan-build-py
底下的工具是鳥的巢狀結構的工具,可以包住實際叫用 Clang。並負責修改指令列。我不熟悉這些系統,也從未受干擾。
Fuchsia 包裝函式指令碼
這個非常小型的殼層指令碼會包裝 Ericsson xtu-build-new
包裝函式。如要對 Zircon 進行全面分析,請務必先進行清理,並指定正確的 Clang 版本路徑。然後在 zircon 目錄中:
ninja -t clean && ninja && ./run.sh
如果只要建構核心,請將 TARGET
指定為環境變數:
ninja -t clean && ninja clean && TARGET=./build-zircon-pc-x64/zircon.elf ./run.sh
並要求 clangify.py 位於具有可執行位元集的 zircon 目錄中。分析完成後,會出現 .result-xtu
目錄,其中包含:
- 一些 Apple plist 檔案,也就是錯誤報告
- 失敗的目錄,內含傳回非零的分析工具叫用 std{out,err};
- 傳遞目錄,其中包含傳回 0 的分析工具叫用 std{out,err}。
查看分析結果
目前如要剖析 plist 報表並透過網頁介面查看這些報表,您只能使用由 Ericsson 開發的 CodeChecker 工具,此工具可用於程式碼理解和其他工作。CodeChecker 需要安裝大量依附元件,建議您使用 pip、npm 或任何替代方法 (而非使用 apt-get) 進行安裝。簡而言之,在執行分析並將 plist 傾印至 .result-xtu 之後,您可以叫用 CodeChecker plist
來剖析 plist:
CodeChecker plist -d .result-xtu -n 2016-12-12T21:47_uniq_name -j 48
每次叫用 CodeChecker plist
時,-n
的引數都不得重複,因為該引數代表單一剖析執行作業。CodeChecker 會申訴其他情況。接著,執行 CodeChecker server
,在 localhost:8001
上啟動網路伺服器,顯示所有先前剖析執行作業的報表。
取得協助
這個 Samsung 修補程式是由 Aleksei Sidorin 和他的團隊編寫。Aleksei 充分瞭解 ASTImporter.cpp
和其他 AST 合併做法,非常實用。他和Sean Callanan很開心地檢查我的 AST 進口商修補程式。此外,Aleksei 也在 2016 年 LLVM 開發人員會議中,討論了以摘要為基礎的跨程序分析。
這個 Ericsson 修補程式是由 Gábor Horváth 和他的團隊所編寫。Gábor 針對如何運用 xtu-build-new
工具進行 CTU 分析,提供了很大的幫助。
我 (Kareem Khazem) 也很樂意提供協助。
LLVM irc 管道也相當實用。
Zircon 特定分析
上游 Clang 非常樂意收到 Zircon 特定 Clang 檢查工具的修補程式。MutexInInterruptContext 檢查工具是其中一個範例 (從 Farid Molazem Tabrizi 編寫的 LLVM 票證上轉移),以及 SpinLockChecker 和 MutexChecker。Clang 檢查的潛在審查人員包括 Devin Coughlin (Apple)、Artem Dergachev (隸屬於 Aleksei Sidorin 的 Samsung 團隊) 和 Anna Zaks (同為 Apple)。
這些檢查工具通常會「選擇加入」。也就是說,您必須傳送旗標至分析器才能啟用:-analyzer-checker=optin.zircon.MutexInInterruptContext
之類的功能。
如果這些修補程式並未附在 Clang 中,則您必須套用這些修補程式。如要使用這些指令碼和 Ericsson 包裝函式指令碼分析 Zircon,請在檔案結尾的 xtu-analyze.py
叫用中加入 -e optin.zircon.MutexInInterruptContext
選項以修改 Fuchsia 包裝函式指令碼。MutexInInterruptContext
的修補程式包含測試套件,可用來進行分析的功能範例。
Zircon 中的 CTU 支援進度
已修正 AST 匯入工具的問題
大多數的 Zircon 檔案發生上游 CSA 當機事件。本節說明 Kareem Khazem 遇到的一些問題和修正方法。
不支援的 AST 節點
Clang Static 分析工具無法匯入大量 Zircon 程式碼,因為無法實作某些類型的 AST 節點匯入功能。下列為支援這些節點的修補程式:
AtomicType | 修補程式已合併至上游 |
---|---|
CXXDependentScopeMemberExpr |
https://reviews.llvm.org/D26904 |
UnresolvedLookupExpr |
https://reviews.llvm.org/D27033 |
DependentSizedArray |
|
CXXUnresolvedConstructExpr |
|
UsingDecl |
https://reviews.llvm.org/D27181 |
UsingShadowDecl |
https://reviews.llvm.org/D27181 |
FunctionTemplateDecl |
https://reviews.llvm.org/D26904 |
一般來說,在實作新節點類型的支援時,必須在 ASTImporter.cpp
中實作 VisitNode
函式,以及單元測試和功能測試。上方 Kareem 的修補程式包含範例。仍有許多不支援的 AST 節點grep 的分析工具輸出目錄 error: cannot import unsupported AST node
。
Ericsson 修補程式集僅包含 Samsung 修補程式集中的 ASTImporter
程式碼子集。在某些情況下,對於不支援的節點,您可以直接從 Samsung 修補程式集取得 Visit
函式。不過,Samsung 修補程式集並未納入任何測試,因此在對該節點的支援之前,您還是需要先編寫測試。
Segfaults galore
ASTImporter.cpp
中的許多程式碼都發生錯誤。Aleksei 有時會針對問題 (例如這本手冊) 提供私人修補程式,因此建議您向 IRC 快速連線偵測 (a-sid) 通知。我的偵錯策略是針對開頭為 analyze: DEBUG: exec command in
(接著是分析工具的實際指令列) 的第二個字串,查看包裝函式輸出,並透過 gdb 執行該指令列。通常只要幾小時的時間,即可追蹤分層來源。
CTU 之前和之後發現的錯誤
VFS 中可能存在的錯誤嗎?
這是 oldparent
的雙重釋放,會在 system/ulib/fs/vfs.c:vfs_rename
上宣告未初始化。兩行之後,系統會呼叫 vfs_walk
(相同檔案) 做為第二個引數,並使用 oldparent
。您可以從 vfs_walk
傳回 (不指派 oldparent
),方法是輸入 for 迴圈,然後在第一個迴圈上點擊 return r
陳述式。如果 r
的值大於零,系統會前往 else if
陳述式,該陳述式會在 oldparent
上呼叫 vn_release
(目前仍未初始化)。
執行緒中可能存在錯誤嗎?
此為使用釋放後記憶體。路徑如下:
kernel/kernel/thread.c:thread_detach_and_resume
- 致電「
thread_detach(t)
」- 回程時間:
thread_join(t, NULL, 0)
- 免費
t
並退回NO_ERROR
- 免費
- 回程時間:
NO_ERROR
- 回程時間:
- 檢查錯誤為 1false1
- 呼叫
thread_resume(t)
,已釋出。thread_resume
會存取t
的欄位。
- 致電「
CTU 偽陽性
- CSA 無法解析透過函式指標呼叫的函式實作。也就是說,這個函式無法假設函式的傳回值可能為何,也無法假設函式對輸出參數造成的影響。
有多種函式類別,分析器無法存取其實作項目。再次提醒您,分析器無法知道這類函式會觸碰輸出引數,因此會刻意回報下列程式碼從垃圾值讀取資料:
struct timeval tv; gettimeofday(&tv, NULL); printf("%d\n", tv.tv_usec); // [SPURIOUS REPORT] Access to // uninitialized variable ‘tv’
以下列舉一些容易遭受這類不精確的函式:
- 系統呼叫 (例如
gettimeofday
) - 編譯器內建項目 (例如
memcpy
)
- 系統呼叫 (例如