A Unifying View on SMT-Based Software Verification — 投影片
原文:JAR 2018 · doi:10.1007/s10817-017-9432-6 · 開啟原文 PDF ↗
0 你需要先會的東西
0.1 一個關於數字的尷尬問題
假設論文 A 說他們的方法在 benchmark 上贏過方法 B 三倍。你該信嗎?
這篇論文有一半的價值在這裡:把四個演算法放進同一份實作、用同一個 solver 跑,這時候的比較才有意義。
這也是為什麼「統一框架」不只是理論上漂亮——它是做出可信實驗的前提。
0.2 先回顧:SSA 與 path formula
SV 1 教過的東西這裡整篇都在用。快速複習:
一條執行路徑翻成公式時,每個變數的每個版本要有自己的名字:
i = 0;
i = i + 1;
$$i_0 = 0 \;\wedge\; i_1 = i_0 + 1$$
0.3 BMC:把迴圈攤平
Bounded model checking 的想法非常直接:迴圈就展開它。
i = 0;
while (i < 3) { i = i + 1; }
assert(i == 3);
展開兩圈的路徑翻成公式:
$$i_0 = 0 \;\wedge\; i_0 < 3 \;\wedge\; i_1 = i_0 + 1 \;\wedge\; i_1 < 3 \;\wedge\; i_2 = i_1 + 1$$沒有迴圈了,就是一條長長的公式,丟給 SMT solver。
- 有解 → 這條路走得通,如果它通往
ERROR就是真的 bug - 無解 → $k$ 步之內沒有 bug
注意 BMC 的先天限制:它只能說「$k$ 步之內沒事」,不能說「永遠沒事」。要證明程式完全正確,BMC 不夠。
0.4 k-induction:把歸納法用在程式上
你在大一數學學過歸納法:證 $P(0)$,再證 $P(n) \Rightarrow P(n+1)$,就得到所有 $n$ 都成立。
套到程式上:
- base case:初始狀態是安全的
- inductive step:安全的狀態走一步之後還是安全的
如果兩者都成立,程式就永遠安全——而且不需要展開迴圈。
k-induction 的解法是把假設加強:不只假設「上一步安全」,而是假設「連續 $k$ 步都安全」,再證第 $k+1$ 步安全。假設越強,能證的東西越多。
0.5 ARG:記住誰是誰的後繼
ARG(abstract reachability graph)就是把探索過程記下來的圖:節點是 abstract state,邊表示「這個是那個的後繼」。
為什麼需要它? 因為找到誤報時,你得知道「這條錯誤路徑是怎麼走過來的」才能修。ARG 就是那份路徑紀錄。SV 1 的 refinement 也需要這個,只是那時沒給它名字。
0.6 precision:每個位置各自的精細度
$\pi$ 是一個從程式位置映到predicate 集合的函數。$\pi(l_4) = \{x = y\}$ 的意思是「在位置 $l_4$,追蹤 $x = y$ 這個 predicate」。
這就是 SV 1 那張「每個位置一份清單」的表,被寫成了框架的一等公民。
以上是前置。接下來是論文本身。
1 這篇要解決的問題
兩個問題,一個理論一個實務:
- BMC、k-induction、predicate abstraction、IMPACT 之間的關係到底是什麼?
- 它們的效能數據能不能公平比較?
第二個問題其實依賴第一個——只有先證明它們是同一個框架的不同設定,「其他變因都相同」的比較才做得出來。
2 在這之前,卡在哪裡
3 關鍵洞察
3.1 先把框架撐大:CPA+ 與 CPA++
SV 2 的 CPA 是四元組。這篇用的是六元組(定義在 §2.2, p.302):
$$\mathbb{D} = (D, \Pi, \rightsquigarrow, \mathsf{merge}, \mathsf{stop}, \mathsf{prec})$$多出來的兩個:
- $\Pi$ —— 所有可能 precision 的集合
- $\mathsf{prec}$ —— precision-adjustment operator,可以在執行途中調整精度
| 世代 | 定義 | 出處 |
|---|---|---|
| CPA | $(D, \rightsquigarrow, \mathsf{merge}, \mathsf{stop})$ | SV 2(CAV'07) |
| CPA+ | $(D, \Pi, \rightsquigarrow, \mathsf{merge}, \mathsf{stop}, \mathsf{prec})$ | ASE'08(本篇引為 [19];定義見 p.302、Algorithm 1 見 p.304) |
| CPA++ | 同上,演算法加 abort 與 fcover | 本篇 Algorithm 2, p.315 |
3.2 blk:什麼時候做 abstraction
這是最能說明問題的一個旋鈕。
Adjustable-Block Encoding(ABE) 把「多久做一次」變成參數,由 block-adjustment operator blk 控制:
| 設定 | 何時做 abstraction | 效果 |
|---|---|---|
| $\mathsf{blk}^{never}$ | 從不 | 整支程式當成一個 block(whole-program encoding) |
| $\mathsf{blk}^{l}$ | loop head 與 error location | |
| $\mathsf{blk}^{lf}$ | 再加上 function call/return | 近似 large-block encoding |
$\mathsf{prec}$ 就是照著 blk 的指示動作(論文 §3.1, p.308):
看起來嚇人,但意思很簡單:blk 說「該做了」就做 abstraction,否則原封不動放行。
3.3 BMC 的完整配方:blk 不是唯一的旋鈕
這裡要很小心,因為一個常見的說法是錯的。
不能說「調一個 blk 就從 predicate abstraction 變成 BMC」。論文 §4.1(p.315)列的 BMC 配方是四個零件一起:
- $\mathsf{blk}^{never}$ —— whole-program encoding
- $\mathsf{fcover}^{id}$ —— 不做 forced covering
- Loop-Bound CPA $\mathbb{LB}$ —— 每個 loop head 配一個計數器,precision 就是 loop bound $k$。它的 transfer relation 刻意是 unsound 的:計數器到達 $k$ 時不產生後繼狀態,用這個方式擋掉超過 $k$ 圈的路徑
- 外層再包一個演算法 —— 在 CPA++ 跑完後檢查每個 abstract error state 的 path formula 是否 satisfiable。另可做 forward-condition check,判斷 $k$ 夠不夠大
而 predicate abstraction 走的是 CEGAR + refinement,是完全不同的外層流程。
為什麼這件事值得計較? 因為論文最重要的立論是「公平比較」,而公平比較的前提正是其他變因都相同、只有一個變因不同。把差別過度簡化成「一個運算子」,反而讓這個立論站不住。
3.4 refine 的四個步驟
論文 §3.2(p.309)把 refinement 拆成四步,這個結構值得原樣記住:
- Abstract-Counterexample Construction —— 從 ARG 回溯出抽象反例 $\langle e_0, \dots, e_n \rangle$
- Feasibility Check —— 把每段的 block formula 串成一條 counterexample formula,丟一次 SMT query。有解就是真 bug,無解就是誤報
- Interpolation —— 求 interpolant 序列(見下)
- Refinement Strategies —— 兩種選擇:
| 策略 | 做法 |
|---|---|
| IMPACT 式 | 把 $\tau_i$ 直接 conjoin 進對應的 abstraction formula,強化既有狀態 |
| Predicate 式 | 抽出 interpolant 的 atoms 當 predicate,加進 precision,從 pivot state(第一個會受新精度影響的狀態)之後重新探索 |
3.5 從一個 interpolant 到一整個序列
- $\tau_0 = \mathit{true}$ 且 $\tau_n = \mathit{false}$
- $\tau_{i-1} \wedge \varphi_i \Rightarrow \tau_i$
- $\tau_i$ 只用同時出現在 $\bigwedge_{j \le i} \varphi_j$ 與 $\bigwedge_{j > i} \varphi_j$ 的變數
第 2 條是關鍵。 它要求整串前後扣得起來:每一個 $\tau_i$ 都能從前一個加上這一段的公式推出來。
3.6 四個演算法的對照
這是論文 Table 1(p.324)的內容,四個欄位照原樣列出:
| 演算法 | 抽象公式表示 | blk | refinement 策略 | fcover |
|---|---|---|---|---|
| BMC | SMT | $\mathsf{blk}^{never}$ | 無 | $\mathsf{fcover}^{id}$ |
| k-induction | SMT | $\mathsf{blk}^{never}$ | 無 | $\mathsf{fcover}^{id}$ |
| predicate abstraction | BDD | e.g. $\mathsf{blk}^{l}$ | Predicate refinement | $\mathsf{fcover}^{id}$ |
| IMPACT | SMT | e.g. $\mathsf{blk}^{l}$ | IMPACT refinement | e.g. $\mathsf{fcover}^{\textsc{Impact}}$ |
表格讀不出來的兩件事,補在這裡。
4 手算一遍
blk 的效果用算的比用讀的清楚。
5 對後續的影響
這篇是 CPAchecker 現在的理論骨架。 上面講的每個零件都對應到實作裡的一個 configuration 選項——四個演算法共用同一份程式碼,差別只在設定檔。
方法論上的影響可能更大。 這篇示範了一件事:要比較演算法,先把它們放進同一份實作、用同一個 solver。帶著這個標準,你之後讀任何驗證工具的論文都可以問一句:這個比較控制了哪些變因?
回頭看三篇的關係:
SV 1 (POPL'04) SV 2 (CAV'07) SV 3 (JAR'18)
predicate 從哪來 → 怎麼統一所有分析 → 統一後能公平比較什麼
Craig interpolation CPA 四元組 CPA+ / CPA++ 與一組旋鈕
└──────── 是這個框架的一種設定 ────────┘
6 讀原文時的導覽
37 頁,是三篇裡最長的。不必一次讀完。
| 章節 | 頁 | 建議 |
|---|---|---|
| §1 Introduction | p.299–301 | 必讀。 動機與貢獻;§1.4 Structure 是全篇地圖 |
| §2 Background | p.301–305 | 必讀。 §2.1 程式表示法、§2.2 CPA(p.302,含 CPA+ 六元組定義)、§2.3 CEGAR(p.305) |
| §3 Predicate CPA | p.305–315 | 必讀。 abstract state 的三元組結構、blk、prec |
| └ §3.2 Refinement | p.309 | 必讀。 四個步驟與 interpolant 序列的定義 |
| └ §3.3 Forced Covering | p.313 | 進階。IMPACT 的加速機制 |
| └ §3.4 An Extended CPA Algorithm | p.314 | 值得看 Algorithm 2(CPA++)與那四項差異 |
| §4 Unifying SMT-Based Approaches | p.315–326 | 挑著讀。 四個方法各一節;四者的設定對照表 Table 1 在 p.324 |
| └ §4.1 Bounded Model Checking | p.315 | 先讀這節就好,BMC 的完整配方在這 |
| └ §4.2 k-Induction | p.316 | |
| └ §4.4 Lazy Abstraction with Interpolants (IMPACT) | p.322 | |
| §5 Evaluation | p.326 起 | 進階。公平比較的實際數據;benchmark 取自 SV-COMP'17 |
建議路徑:§1 → §2 → §3.1 → §3.2 → §4.1,其餘按需要再回頭。
取得原文:台大校內網路可直接下載,見頁首 DOI。