注意
這條博客目前還非常不完善,可能存在一些錯誤,待后續完善
動機
編譯時的優化。
編譯器可以只根據本地信息進行一些優化。例如,考慮以下代碼。
x = a + b; x = 5 * 2;
優化器很容易識到,x的第一個賦值是一個 "無用的 "賦值,因為為x計算的值從未被使用(因此第一個語句可以從程序中刪除)表達式5*2可以在編譯時計算出來,將第二個賦值語句簡化為x=10。
然而,有些優化需要更多的 "全局 "信息。例如,考慮下面的代碼。
a = 1; b = 2; c = 3; if(...)x = a + 5 else x = b + 4 c = x + 1
在這個例子中,對c的初始賦值(在第3行)是無用的,表達式x + 1可以簡化為7,但編譯器如何發現這些事實就不太明顯了,因為不能只看一兩個連續的語句來發現。需要進行更全面的分析,以便編譯器知道在程序的每個point上哪些變量可以保證有常量值,以及哪些變量在被重新定義之前會被使用。
為了發現這些類型的屬性,我們使用數據流分析。數據流分析通常是在程序的控制流圖(CFG)上進行的;目標是將每個程序組件(CFG的每個節點),與保證在所有可能的執行中在該點上保持的信息起來。(即獲取在任何可能的執行情況中,都確定的信息,如上面的代碼中 c=7 是確定的)
理解
概念
DFA是一種靜態分析手段。數據流分析指的是一組,用來獲取有關數據如何沿着程序執行路徑流動的相關信息,的技術.
Data-flow analysis is a technique for gathering information about the possible set of values calculated at various points in a computer program. 通俗的理解為,DFA 可以計算出程序每個point(如基本塊出入口)對應的一組值(狀態),而這組值包含了一些數據相關的信息。通過這些信息,可以解決一些問題:比如編譯優化問題,比如檢測是否存在use after free
分類
根據對程序路徑的分析精度分類:
- 流不敏感分析(flow insensitive):不考慮語句的先后順序,按照程序語句的物理位置從上往下順序分析每一語句,忽略程序中存在的分支
- 流敏感分析(flow sensitive):考慮程序語句可能的執行順序,通常需要利用程序的控制流圖(CFG)
- 路徑敏感分析(path sensitive):不僅考慮語句的先后順序,還對程序執行路徑條件加以判斷,以確定分析使用的語句序列是否對應着一條可實際運行的程序執行路徑
根據分析程序路徑的深度分類:
- 過程內分析(intraprocedure analysis):只針對程序中函數內的代碼
- 過程間分析(inter-procedure analysis):考慮函數之間的數據流,即需要跟蹤分析目標數據在函數之間的傳遞過程
- 上下文不敏感分析(context-insensitive):將每個調用或返回看做一個 “goto” 操作,忽略調用位置和函數參數取值等函數調用的相關信息
- 上下文敏感分析(context-sensitive):對不同調用位置調用的同一函數加以區分
前向分析和后向分析:
根據節點狀態的含義不同,可以分為前向數據流分析和后向數據流分析。
數據流分析方法
前向數據流分析:在前向流分析中,一個塊的退出狀態是該塊進入狀態的一個函數,這個函數是塊中的語句效果的組成。一個塊的進入狀態是其前輩的退出狀態的函數。這就產生了一組數據流方程。
對程序進行數據流分析的一個簡單方法是為控制流圖的每個節點設置數據流方程,並通過反復計算每個節點本地輸入的輸出來解決這些問題(計算方法依賴人為設定的規則),直到整個系統穩定下來(狀態不再改變),即達到一個fixpoint。這種一般的方法,也被稱為Kildall方法.
對於不同的問題(比如動機中的編譯優化問題,以及其他很多問題呢,比如檢測是否存在use after free),設置不同的狀態表示(如基本塊出入口變量的賦值情況)、狀態轉換規則,最終獲取相關問題的信息,從而解決問題。
利用數據流分析挖掘漏洞
實際問題舉例
如檢測變量賦值,編譯優化的問題,設置初始狀態為 變量賦值方程 ,定義狀態變化的規則。然后沿着CFG分析,根據塊改變狀態,最后系統穩定(狀態不再改變)后,可以得到信息。比如確定變量是否是常量(x=5),還是由哪些變量賦值(x=a+1)。參考前面提到的動機
如檢查是否存在指針use after free的問題
int contrived(int *p, int *w, int x) { int *q; if (x) { kfree(w); // w free q = p; } [...] if (!x) return *w; return *q; // p use after free } int contrived_caller(int *w, int x, int *p) { kfree(p); // p free [...] int r = contrived(p, w, x); [...] return *w; // w use after free }
下面是用於檢測指針變量錯誤使用的檢測規則:
v 被分配空間 ==> v.start v.start: {kfree(v)} ==> v.free v.free: {*v} ==> v.useAfterFree v.free: {kfree(v)} ==> v.doubleFree
分析過程從函數 contrived_call 的入口點開始,對於過程內代碼的分析,使用深度優先遍歷控制流圖的方法,並使用基本塊摘要進行輔助,而對於過程間的分析,選擇在遇到函數調用時直接分析被調用函數內代碼的方式,並使用函數摘要。
函數 contrived 中的路徑有兩條:
- BB0->BB1->BB2->BB3->BB5->BB6:在進行到 BB5 時,BB5 的前置條件為 p.free, q.free 和 w.free,此時語句
q1 = *q
將觸發 use-after-free 規則並設置 q.useAfterFree 狀態。然后返回到函數 contrived_call 的 BB2,其前置條件為 p.useAfterFree, w.free,此時語句w1 = *w
設置 w.useAfterFree。 - BB0->BB1->BB3->BB4->BB6:該路徑是安全的。
即為三個指針p,q,w設置對應 狀態 ,然后根據檢測規則改變狀態。當系統穩定,根據指針對應狀態是否是useAfterFree,可以確定是否存在use after free問題。
參考
博客:
https://www.bookstack.cn/read/CTF-All-In-One/doc-5.4_dataflow_analysis.md
https://chhzh123.github.io/blogs/2019-02-15-spa-ufmg/#%E5%8F%82%E8%80%83%E6%96%87%E7%8C%AE-1
https://blog.csdn.net/z2664836046/article/details/88742210
wiki:
https://en.wikipedia.org/wiki/Data-flow_analysis
大學的課程:
https://pages.cs.wisc.edu/~horwitz/CS704-NOTES/2.DATAFLOW.html
https://groups.seas.harvard.edu/courses/cs252/2011sp/slides/Lec02-Dataflow.pdf
http://www.cs.columbia.edu/~suman/secure_sw_devel/Basic_Program_Analysis_DF.pdf
https://suif.stanford.edu/~courses/cs243/lectures/l2.pdf
論文:
Gary A. Kildall. 1973. A unified approach to global program optimization. In Proceedings of the 1st annual ACM SIGACT-SIGPLAN symposium on Principles of programming languages (POPL '73). Association for Computing Machinery, New York, NY, USA, 194–206. DOI:https://doi.org/10.1145/512927.512945
Markus Mohnen: A Graph-Free Approach to Data-Flow Analysis. CC 2002: 46-61