Sparse Texture Memory 技术介绍
Sparse Texture Memory的原理是使用一种虚拟的纹理地址空间,它可以映射到实际的物理纹理地址空间。虚拟的纹理地址空间可以比物理的纹理地址空间大得多,但是只有部分的虚拟地址是有效的,也就是说,只有部分的纹理数据是存在的。这些有效的虚拟地址对应于一些tiles,它们可以被加载到物理的纹理地址空间中,或者从中卸载出来。这个过程可以由应用程序或者驱动程序控制,以根据视图的变化或者其他因素来动态地调整纹理的分配。
一、Android GPU 安全现状
针对GPU driver发起过的重大0/1/n day攻击:
-
CVE-2022-22706是一个影响Arm Mali GPU内核驱动程序的安全漏洞,它允许非特权用户对只读内存页进行写入操作;
-
CVE-2023-4211是一个UAF漏洞,本地非特权用户可以进行不正确的 GPU 内存处理操作来访问已释放的内存;
-
CVE-2023-33106、CVE-2023-33107、CVE-2022-22071、CVE-2023-33063等多个漏洞,影响 Qualcomm 公司 GPU 和 DSP 驱动程序,并发现在野利用,由 Google 威胁分析小组 (Threat Analysis Group,TAG) 和Project Zero 团队向 Qualcomm 公司报告,这些漏洞可能受到有限的、有针对性的利用 。
目前Android GPU缺乏足够的关注与研究,主要体现在:
-
复杂,GPU的结构和行为很难理解和预测,这给攻击和防御都带来了挑战;
-
专有,GPU的设计和实现往往都是商业机密,不公开给外界,这限制了研究者和用户对GPU的了解和控制;
-
新功能和特性,GPU发展很快,不断地引入新技术和应用,带来了新的安全风险和漏洞。
二、Android GPU市场占比
PowerVR GPU的性能不如一些高端的GPU,例如Adreno和Mali。因此在一些低端或中端的手机,平板,和智能电视上比较流行,例如三星A12,红米9a/10a,摩托Pure G,和Fire TV 。PowerVR GPU优点是功耗低,性能稳定,支持多种图形API,例如OpenGL ES,Vulkan,和Metal 。此外,PowerVR GPU曾经是苹果的GPU供应商,但后来苹果决定自己设计GPU,不再采用PowerVR GPU。但到目前为止,仍有超过35%的智能手机使用了PowerVR GPU,它可以满足客户对图形和计算的需求,同时不断创新和突破。可以说PowerVR GPU在智能手机上的应用,推动了移动游戏的发展。
总的来说,对于PowerVR GPU的安全性研究在2022年有了一些进展,但还不够充分,还需要更多的外部安全研究人员的参与,以提高PowerVR GPU的安全性水平,保护安卓设备的用户。
三、Android 图形处理技术
下图很好的展示了安卓系统上的图形处理的流程和组件:
从左边的高级图形库开始,经过一些闭源的图形库,例如libegl.so、ligGLESv3.so等,到达右边的GPU用户模式驱动,例如libEGL_mtk.so与libsrv_um.so,然后再到GPU内核驱动和GPU固件。其中不同的GPU厂商有不同的用户模式驱动的实现,这可能会影响图形性能和安全性。
OpenGL是一个用于渲染2D和3D图形的跨语言、跨平台的API。它由近350个不同的函数调用组成,可以创建和操作图形对象,如纹理、缓冲区、着色器等。不同的图形硬件厂商可以提供符合OpenGL规范实现的驱动程序。ImgTec 和 MediaTek 公司提供实现的 OpenGL ,在OpenGL API与后端GPU驱动程序与固件之间包含了一部分中间件,这部分中间件用于管理主机和设备的内存,过滤和检查用户的输入和当前状态,以及提供日志和监测功能。如下图:
在后端部分,PVR driver是OpenGL API的具体实现。也就是说,用户应用程序会调用OpenGL API,再由OpenGL API调用PVR driver,最后PVR driver对GPU固件进行图形渲染。PVR driver主要由两部分组成,PMR与MMU:
-
PMR(Physical Memory Resource)用于管理分配给GPU的物理内存页面,它可以封装不同类型的内存来源,如连续的或离散的物理内存,以便访问和操作;
-
这里MMU(Memeory Management Unit)是指GPU的内存管理单元,功能与CPU MMU相同,负责将虚拟地址转换为物理地址,以及处理缺页异常和缓存一致性等问题。MMU又可细分为三个部分: 1. MMU Context对象是一种用于表示GPU的MMU上下文的结构,它包含了一些信息,如MMU的配置参数,MMU的页表基址,以及MMU的状态和统计数据等; 2. GPU Memory Heap是一种用于表示GPU的内存堆的结构,它包含了一些信息,如堆的大小,堆的属性,堆的分配策略,以及堆中的空闲和已用内存块等; 3. GPU Memory Reservation是一种用于预留GPU内存的机制,它可以在创建PMR时指定一定数量的内存页面,以保证在需要时可以分配给PMR。这样可以避免在运行时出现内存不足的情况,提高性能和可靠性。
下图用于说明PVR驱动程序如何通过PMR(Physical Memory Resource)管理分配给GPU的物理内存页面,并通过mmap函数将其映射到CPU的虚拟内存空间,以便应用程序可以访问和操作。图中的数字表示不同的内存地址,如0x800010000和0x800020000是GPU的虚拟地址,0x410000和0x420000是CPU的虚拟地址,PMR持有的16个物理页面是在这两个虚拟地址空间之间进行映射的,以便应用进行访问:
以上即是安卓图形处理的具体流程、组件和原理。
四、针对Android GPU进行漏洞挖掘
针对Android GPU漏洞挖掘,首先想到了以下几种常见的方式:
-
逆向分析so库文件及其API函数,追踪API函数 -
通过hook PLT函数,改变参数变量等的方式对API进行Fuzz -
在bug tracker中分析用户使用OpenGL时导致的内核崩溃报告
在GL_EXT_sparse_texture扩展的规范中提到,如果TexPageCommitmentEXT的commit参数值是False,那么纹理区域就会被取消提交(de-committed)。取消提交一个纹理区域意味着释放它的物理存储空间,并使它的内容变得未定义。此时OpenGL的行为会发生变化,这样做的目的是为了节省内存资源,或者为了重新提交(re-commit)一个不同的纹理区域。一般来说,OpenGL的行为分为四种情况,分别是:
-
如果commit的值是False,那么纹理区域就会被取消提交,它的物理存储空间就会被释放,它的内容就会再次变成未定义。
-
从未提交的的区域读取数据会产生未定义的数据,但是不会有其他不良的影响。
-
在未提交的区域上执行带有返回值的原子操作会正常完成,但是返回的值会是未定义的,而且操作的结果会被丢弃。
-
向这样的区域写入数据会被忽略。OpenGL可能会尝试向未提交的区域写入数据,但是这样做的效果是无害的。
在分配之后,glTexSubImage3D和memcpy函数在GPU虚拟内存和CPU虚拟内存之间通过物理内存传输数据。下图中的代码片段是调用glTexSubImage3D函数,将ptr参数设置为image_ptr,表示将纹理数据从CPU内存复制到PMR物理内存:
当CPU虚拟内存unmap时,会从PMR物理内存取消映射。当TexPageCommitmentEXT的commit参数为GL_FALSE时,将会销毁纹理内存。
这一切看起来都没什么问题,我们无法重新将销毁的稀疏纹理内存从GPU重新映射到CPU。但如果我们从CPU访问未定义的内存,如下图:
这会导致一些问题:
3、导致GPU虚拟内存从用户内存开始
PowerVR支持不同的page大小,4K, 16K, 64K, 256K, 1M, 2M。利用以上流程,当GPU虚拟内存的page大小与PMR的page大小不同时,将导致OOB漏洞:
References
原文始发于微信公众号(山石网科安全技术研究院):Evils in the Sparse Texture Memory