Quadrooter安全漏洞-遇見多線程,高通也栽坑

臨近周末,回顧了下安全圈的新聞,發現最勁爆的當屬「Quadrooter」漏洞(當然演藝圈更勁爆的事件和咱們無關)。這個漏洞由四個子漏洞組成,由於高通佔據了Android市場65%以上的份額,所以這個漏洞導致約9億Android設備受到影響。本篇文章將探討Quadrooter中的多線程問題,以及研發階段的防範策略。

漏洞潛在風險:惡意攻擊,黑客將能夠快速提升至Root許可權,然後在你的手機中胡作非為。

影響:所有使用高通源代碼的親們...

修復:等待各大廠商推送補丁(相信我,這流程一定很漫長)。

1.緣起

先來看看Quadrooter的英文詳情描述(注意加粗的部分):

CVE-2016-2503 and CVE-2106-2504 are related to Qualcomms GPU component Kernel Graphics Support Layer:

CVE-2016-2503:

One of the GPU components - Kernel graphic Support Layer - has a module kgsl_sync that is responsible for syncing between CPU and the apps. Within this module is a function that is prone to race condition flaw that can be exploited

CVE-2016-2504:

A user space process can allocate and map memory to GPU, thereby it can create/destroy kgsl_mem_entry which represents an object that uses GPU memory. This object is bound to a process using GPU mapping mechanism or the "idr" mechanism. But since there is no access protection enforced, this object can be freed by another thread.

CVE-2016-2059:

The vulnerability is present in a kernel module introduced by Qualcomm called ipc_router that provides inter-process communication where it is possible to convert a regular socket (CLIENT_PORT) into a monitoring socket (CONTROL_PORT).

CVE-2016-5340:

Ashmem is Androids memory allocation sybsystem that enables processes to efficiently share memory buffers. Devices using Qualcomm chipsets use a modified version of ashmem, the vulnerability is in one of the functions in this version of ashmem.

Attackers can trick get_ashmem_file function to think that an arbitrary file called "ashmem" is actually an ashmem file.

大家可以看到了漏洞2503和2504的罪魁禍首:Race Condition與Access Protection Loss,這是本篇文章想要介紹的重點內容:讓高通栽坑的多線程。

當然我們也對漏洞2509和5340進行了分析,認為是邏輯性的Bug...這個真愛莫能助。

多線程的使用和潛在風險

多線程是多任務處理的一種特殊形式,多任務處理允許讓電腦同時運行兩個或兩個以上的程序。一般情況下,兩種類型的多任務處理:基於進程和基於線程。

1.基於進程的多任務處理是程序的並發執行。

2.基於線程的多任務處理是同一程序的片段的並發執行。

多線程程序包含可以同時運行的兩個或多個部分。這樣的程序中的每個部分稱為一個線程,每個線程定義了一個單獨的執行路徑。

算了,還是用白話文解釋下吧,我在知乎上回答過一個問題(多線程有什麼用? - 韓葆-逸松的回答),以吃飯為由舉了個例子:假設很多人需要吃飯,桌子不夠,那麼桌子和餐具就是緊缺資源(專業術語叫做信號量),每個人吃飯,就相當於一件又一件的流水事情-叫做線程。普通的單線程就是整個餐廳只有一個單人桌,這個人吃完了,下一個人輪上。但大餐館用的可能是八仙桌(好吧我比較喜歡這種古代的方正桌子),同時能容納八個人吃飯,那麼這件事情就從一次一個變成了一次多個或者多次多個,也就是多線程。如何安排吃飯順序對管理人員來說是個巨大的挑戰,主要問題在於對資源-桌子或者餐具的安排上,舉幾個例子:

1.死鎖-大家都在等著吃飯,但需要特定條件才能開吃:桌一等著桌二的碗,桌二等著桌一的筷子。於是這兩桌就都不能吃飯。狀態停滯了,專業術語叫做「死鎖」。看看,就是它:

2.非原子變數更新-空閑桌子的數字是要加以保護的,類似於特定人員才能修改,假如沒有保護,空桌子一會兒是四個,一會兒是五個,最終的數字往往是不正確的,服務員要被玩死-這就是多線程的加鎖操作。這個問題造成的結果,請看來無影去如風的服務員和苦逼等吃的顧客:

3.競態條件

兩個或多個進程對共享的數據進行讀或寫的操作時,最終的結果取決於這些進程的執行順序。

出現這個問題也比較容易接受,因為最基本的多線程程序,系統只保證線程同時執行,至於哪個先執行,哪個後執行,或者執行中會出現一個線程執行到一半,就把CPU的執行權交給了另外一個線程,這樣線程的執行順序是隨機的,不受控制。但執行的結果在很多實際應用中是不能被接受的,例如銀行的應用,兩個人同時取一個賬戶的存款,一個使用存摺、一個使用卡,這樣訪問賬戶的金額就會出現問題。或者是售票系統中,如果也這樣就出現有人買到相同座位的票,而有些座位的票卻未售出。呵呵,別問我怎麼知道的:

還有其他幾種,就不多說了,多線程的調度和使用是一個坑,是否能用好是程序員水平高低的一個標誌。

Coverity的靜態分析解決方案

代碼層級的問題,自然要在代碼研發階段解決,我們來看看Coverity面向多線程問題的代碼靜態分析解決方案。

Coverity在分析的過程中有一個單獨的選項"--concurrency",面向潛在的多線程問題,包含多種規則(ATOMICITY,BAD_LOCK_OBJECT,BAD_CHECK_OF_WAIT_COND,DC.DEADLOCK,LOCK,LOCK_EVASION,LOCK_INVERSION,MISSING_LOCK),簡單的舉幾個例子:

1.ATOMICITY:檢測代碼包含兩個按順序排列的信號量關鍵區(critical sections),但看起來應該將其合併成一個關鍵區(這是因為後一個關鍵區使用在前一個關鍵區中計算的數據,但在兩者之間可能失效)的一些情況。這是一種並發競態條件形式。對於 C/C++,此規則可查找未為關鍵區提供足夠大小以保護變數的一類缺陷。例如,假設各次讀取和寫入通過鎖保護,但整個操作未得到保護。此類情況可能導致不一致的結果。對於 Java,此檢查器可檢查在關鍵區中定義值 v 的情況。如果 v 流入使用 v 的另一個關鍵區(使用定義 v 時所用的同一個鎖),該檢查器將報告缺陷。此檢查器可跟蹤通過同步方法、同步塊和java.util.concurrent.locks.Lock 對象定義的關鍵區。

示例:

2.BAD_CHECK_OF_WAIT_COND(Java):可檢測線程未正確檢查等待條件即在互斥體中調用 wait() 的很多情況。在Java 中,不會為 wait() 調用提供等待程序需要的條件,因此程序員需要確保等待條件在等待之前為false,在等待之後為 true。此檢查需要在鎖定區域內等待之前執行。否則,等待條件可能在檢查和等待之間變成 true,這會導致程序出現不必要的等待。此外,Java 語言容易發生"虛假喚醒",即 wait() 在收到說明對 notify() 或 notifyAll() 的調用已成功滿足等待條件的通知之前成功返回。因此,還需要檢查循環內等待條件的狀態,這可允許線程在發生虛假喚醒時再次等待。此規則可查找此類檢查執行不當的情況。

示例

3.BAD_LOCK_OBJECT:檢測通過鎖定不當鎖表達式(例如 interned 字元串、裝箱 [boxed] Java 原語或者其內容可能會在執行關鍵區期間發生更改的欄位)保護關鍵區的很多情況。鎖定此類錯誤的鎖對象可能導致不確定的行為或死鎖。

示例:

4.DC.DEADLOCK:可檢測通過容易導致死鎖的方式從 java.lang.Thread 和java.lang.ThreadGroup 中調用已廢棄的方法 suspend() 和 resume()的情況。此外,還會檢測對 java.lang.Thread.countStackFrames() 和java.lang.ThreadGroup.allowThreadSuspension(boolean) 的調用,因為它們依賴suspend() 方法。

5.GUARDED_BY_VIOLATION:可查找欄位在無鎖時進行更新,從而導致潛在競態條件(這可能導致無法預測或不正確的程序行為)的很多情況。GUARDED_BY_VIOLATION 推斷保護關係會記錄欄位在具有已知鎖時進行更新的情況。如果欄位 g 用來保護欄位 f,則訪問 f 需要先持有 g 作為鎖。如果該檢查器推斷鎖在至少 70% 的時間裡保護欄位,或者總共只訪問了欄位三次,其中兩次訪問需要受保護(在此類情況下,欄位會被推斷為受保護),此類資源若出現無鎖保護訪問,則會報告缺陷。如果總共只訪問了欄位兩次,並且只有一次訪問受保護,這種情況下使用 --checker-option=LOCK_FINDER:report_one_out_of_two 針對不受保護的訪問報告缺陷。

在下面的示例中,GuardedByViolationExample.lock 保護 count。如果該檢查器推斷GuardedByViolationExample.lock 保護 count,則會在 decrement() 中報告缺陷。

6.LOCK:LOCK 可查找鎖/互斥體已獲取但未被釋放,或者鎖/互斥體在不使用居間釋放的情況下被鎖定了兩次的很多情況。此類問題通常是由於錯誤處理路徑未能釋放鎖導致的。結果通常是死鎖。支持以下兩種類型的鎖定:

a.專用 (Exclusive):無法通過遞歸方式獲取互斥鎖,任何此類嘗試都會導致死鎖。

b.遞歸 (Recursive):同一線程可以通過遞歸的方式獲取遞歸鎖。

鎖可能是函數的全局變數或本地變數。

當發生以下序列事件時,LOCK 會報告缺陷:

1. 變數 L 被鎖定 (+lock)。

2. L 未被解鎖 (-unlock)。

現在可能發生以下其中一種情況:

-到達了路徑結尾 (-lock_returned),並且 L 沒有出現在函數返回值或其表達式中的任何位置。

-L 再次被鎖定 (+double_lock)。(僅適用於互斥鎖。)

對於特意鎖定函數參數的函數,不會報告任何錯誤。

當發生以下序列事件時,也會報告缺陷:

1. L 被解鎖 (+unlock)。

2. L 被傳遞給斷言持有鎖 L 的函數 (+lockassert)。

忘記釋放已獲取的鎖可能導致程序掛起:後續嘗試獲取鎖會失敗,因為程序在等待絕不會發生的釋放。

7.LOCK_EVASION:LOCK_EVASION 可查找代碼規避鎖(通過檢查線程共享數據的一部分防止修改線程共享數據)獲取或充分持有的很多情況。此類規避可能包括不獲取鎖或提前釋放鎖(不保護修改本身)。規避通過這種方式持有鎖可能允許在運行時交錯或重新排序操作,進而導致發生數據競態。除了報告其他不正確的鎖模式之外,此檢查器還可報告有關雙重檢查鎖模式的缺陷。此經過充分研究的idiom 包括針對非易失性欄位的 null 檢查,後接同步塊輸入,之後再接同一 null 檢查。此模式在 Java中存在重大缺陷,而且在 C# 中被視為不安全且不必要的編碼做法。在幾乎所有 Java 示例中,都可能發生數據損壞;當不可能時,通常可以將相應代碼移除,以獲取相同的效果,並且提高效率和清晰度。讀取損壞數據最常見的方式是:執行相應代碼的第一個線程發現欄位為 null(兩次),並將該欄位賦值給新構造的對象。第二個線程進入代碼,發現欄位不為 null。然後,它在未持有保護該對象創建和初始化的同一鎖的情況下,讀取被引用對象的數據欄位。在此類情況下,Java 內存模型並不保證第二個線程一定會讀取完全初始化的數據,即使它讀取的是已更新的對象引用。編譯器或 CPU 可能記錄寫入以及/或者內存子系統可能未按順序繁殖它們。

8.LOCK_INVERSION:LOCK_INVERSION 可查找程序在不同位置按不同順序獲取鎖/互斥體對的很多情況。如果兩個線程同時使用相反的獲取順序,則此問題可能導致死鎖。例如,下面的序列形成了會導致程序掛起的死鎖。

1. 線程 1 獲取並持有鎖 A 同時嘗試獲取鎖 B。

2. 線程 2 獲取並持有鎖 B 同時嘗試獲取共享鎖 A。

9.MISSING_LOCK:MISSING_LOCK 可查找變數或欄位通常通過鎖/互斥體保護,但它們至少有一次是在未持有鎖時被訪問的很多情況。這是一種並發競態條件形式。競態條件可能導致無法預測或不正確的程序行為。MISSING_LOCK 可跟蹤在持有鎖時更新變數的情況。如果發現某個變數更新未持有鎖,但通常應該持有鎖,則會報告缺陷。

後記

以為很簡單的問題,後來才發現分析起來並不容易,現在的問題在於很難完全看懂高通出問題的源代碼,希望大家能一起探討。

當然也期待大家的支持,逸松(Bob Han)目前已經成為了Synopsys Software Integrity Group北方大區的客戶經理,我們的產品線目前包括靜態分析工具Coverity,網路協議Fuzzing產品Defensics,已知安全漏洞與代碼版權分析工具ProteCode,滲透測試工具Seeker。

各位如果感興趣的話歡迎聯繫15210834682,Bao.Han@synopsys.com。

我有必要吐槽下知乎的編輯器。。。真要命。3

推薦閱讀:

st2 046漏洞利用sh and py腳本(搞起來)
兒童電話手錶的安全漏洞會造成哪些問題?如何避免
「幽靈」和「熔斷」,究竟是什麼?(事件匯總)
Apache OpenOffice更新修復四個中危漏洞
千年蟲之後最大危機,Intel晶元漏洞這隻黑天鵝背後福禍難料

TAG:漏洞挖掘 | 安全漏洞 | 代码质量 |