关闭

Quadrooter安全漏洞-遇见多线程,高通也栽坑

标签: coverity破解coverityQuadrooter多线程静态分析
884人阅读 评论(0) 收藏 举报
分类:

临近周末,回顾了下安全圈的新闻,发现最劲爆的当属“Quadrooter”漏洞(当然演艺圈更劲爆的事件和咱们无关)。这个漏洞由四个子漏洞组成,由于高通占据了Android市场65%以上的份额,所以这个漏洞导致约9亿Android设备受到影响。本篇文章将探讨Quadrooter中的多线程问题,以及研发阶段的防范策略。
漏洞潜在风险:恶意攻击,黑客将能够快速提升至Root权限,然后在你的手机中胡作非为。
影响:所有使用高通源代码的亲们...
修复:等待各大厂商推送补丁(相信我,这流程一定很漫长)。

缘起

先来看看Quadrooter的英文详情描述(注意标红加粗的部分):

CVE-2016-2503 and CVE-2106-2504 are related to Qualcomm's 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 Android's 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.
基于线程的多任务处理是同一程序的片段的并发执行。

多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。
算了,还是用白话文解释下吧,我在知乎上回答过一个问题(https://www.zhihu.com/question/19901763/answer/92891514),以吃饭为由举了个例子:假设很多人需要吃饭,桌子不够,那么桌子和餐具就是紧缺资源(专业术语叫做信号量),每个人吃饭,就相当于一件又一件的流水事情-叫做线程。普通的单线程就是整个餐厅只有一个单人桌,这个人吃完了,下一个人轮上。但大餐馆用的可能是八仙桌(好吧我比较喜欢这种古代的方正桌子),同时能容纳八个人吃饭,那么这件事情就从一次一个变成了一次多个或者多次多个,也就是多线程。如何安排吃饭顺序对管理人员来说是个巨大的挑战,主要问题在于对资源-桌子或者餐具的安排上,举几个例子:


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。

各位如果感兴趣的话欢迎联系我:13311307163,Bao.Han@synopsys.com。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:11594次
    • 积分:319
    • 等级:
    • 排名:千里之外
    • 原创:18篇
    • 转载:1篇
    • 译文:3篇
    • 评论:0条