Evils in the Sparse Texture Memory

本文内容来自BlackHat Europe议题 [1],介绍关于安卓GPU的攻击面。

Sparse Texture Memory 技术介绍

Sparse Texture Memory是一种处理过大的纹理的技术,它可以将非常大的纹理,例如地形、环境或者角色的纹理分割成小的矩形区域,称为tiles,并且只在需要时加载它们,而不需要占用过多的图形内存。这样,可以提高图形的质量和细节,同时避免内存溢出或者分页的问题。

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发展很快,不断地引入新技术和应用,带来了新的安全风险和漏洞。
在Google Project Zero团队的文章《Mind the Gap》[2] 中提到,他们在ARM Mali GPU驱动程序中发现了六个可利用的漏洞,其中一个是之前被修复的漏洞的变种。这些漏洞可以让攻击者在安卓应用中执行本地代码,从而控制物理内存,修改内核内存,泄露物理地址,甚至获取root权限。ARM在2022年7月和8月修复了这些漏洞,并在其网站上公开了漏洞信息和补丁代码。但是,截至2022年11月,这些补丁还没有被安卓厂商推送给终端用户,导致数百万的安卓设备仍然处于危险之中。由此可见,厂商和用户对于安全漏洞修复的重视程度不高,让攻击者有了可乘之机。

二、Android GPU市场占比

目前主流的Android GPU包括ARM公司的Mali系列GPU,Qualcomm公司的Adreno GPU,以及Imagination的PowerVR。

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在智能手机上的应用,推动了移动游戏的发展。

Evils in the Sparse Texture Memory

上图为 2021 年出货量最多的智能手机统计数据,三星A12、A02,红米9a总量超过了9600万台,并且它们都使用了PowerVR GPU。
PowerVR GPU的安全性研究在2022年之前比较有限,没有太多的公开的漏洞报告或分析。2022年谷歌的安卓安全团队和Project Zero团队发现了一些影响PowerVR GPU的漏洞 [3],在PowerVR GPU的安全性研究上发挥了重要的作用。

Evils in the Sparse Texture Memory

总的来说,对于PowerVR GPU的安全性研究在2022年有了一些进展,但还不够充分,还需要更多的外部安全研究人员的参与,以提高PowerVR GPU的安全性水平,保护安卓设备的用户。


三、Android 图形处理技术

下图很好的展示了安卓系统上的图形处理的流程和组件:

Evils in the Sparse Texture Memory

从左边的高级图形库开始,经过一些闭源的图形库,例如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驱动程序与固件之间包含了一部分中间件,这部分中间件用于管理主机和设备的内存,过滤和检查用户的输入和当前状态,以及提供日志和监测功能。如下图:

Evils in the Sparse Texture Memory

值得注意的是,日志和监测功能可能会包含一些内核指针,这可能会造成内存泄露。

在后端部分,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。这样可以避免在运行时出现内存不足的情况,提高性能和可靠性。
下图示例很好的说明了MMU与PMR的工作关系。当前已经注册的GPU虚拟内存从0x8000000000到0x8000100000(阴影部分),预留GPU内存从0x8000010000到0x8000020000(黄色部分),总计16个页交给PMR进行管理,这一切都由PVR driver的PVRSRVBridgeDevmemIntMapPMR函数完成,最终可追溯到安卓系统函数MMU_MapPages:

Evils in the Sparse Texture Memory

下图用于说明PVR驱动程序如何通过PMR(Physical Memory Resource)管理分配给GPU的物理内存页面,并通过mmap函数将其映射到CPU的虚拟内存空间,以便应用程序可以访问和操作。图中的数字表示不同的内存地址,如0x800010000和0x800020000是GPU的虚拟地址,0x410000和0x420000是CPU的虚拟地址,PMR持有的16个物理页面是在这两个虚拟地址空间之间进行映射的,以便应用进行访问:

Evils in the Sparse Texture Memory

以上即是安卓图形处理的具体流程、组件和原理。


四、针对Android GPU进行漏洞挖掘

针对Android GPU漏洞挖掘,首先想到了以下几种常见的方式:

  1. 逆向分析so库文件及其API函数,追踪API函数
  2. 通过hook PLT函数,改变参数变量等的方式对API进行Fuzz
  3. 在bug tracker中分析用户使用OpenGL时导致的内核崩溃报告
对于以上第一、二种方法,由于库中过多的API数量,以及某些晦涩难懂的API原因,导致漏洞挖掘效果并不理想。在使用第三种方法后,找到了一些API导致crash的信息:

Evils in the Sparse Texture Memory

通过分析此文章内容,得知OpenGL API中存在着一些未定义的行为,例如GL_EXT_sparse_texture函数,它由Nvidia在2013年提出,并于2015年3月27日完成。它是一个OpenGL ES扩展,它允许将图形处理器的地址空间(预留)与所有纹理必须物理备份(提交)的要求分离。
在GL_EXT_sparse_texture的新功能中 [4],有一个布尔参数commit,如下:

Evils in the Sparse Texture Memory

在GL_EXT_sparse_texture扩展的规范中提到,如果TexPageCommitmentEXT的commit参数值是False,那么纹理区域就会被取消提交(de-committed)。取消提交一个纹理区域意味着释放它的物理存储空间,并使它的内容变得未定义。此时OpenGL的行为会发生变化,这样做的目的是为了节省内存资源,或者为了重新提交(re-commit)一个不同的纹理区域。一般来说,OpenGL的行为分为四种情况,分别是:

  • 如果commit的值是False,那么纹理区域就会被取消提交,它的物理存储空间就会被释放,它的内容就会再次变成未定义。

  • 从未提交的的区域读取数据会产生未定义的数据,但是不会有其他不良的影响。

  • 在未提交的区域上执行带有返回值的原子操作会正常完成,但是返回的值会是未定义的,而且操作的结果会被丢弃。

  • 向这样的区域写入数据会被忽略。OpenGL可能会尝试向未提交的区域写入数据,但是这样做的效果是无害的。
下图展示了GL_EXT_sparse_texture函数分配稀疏纹理内存的过程,GPU虚拟内存的地址空间,以及如何将虚拟地址(VA)块连接到物理地址(PA)块。图片中。图片中的代码片段是调用glTexPageCommitmentEXT函数,将commit参数设置为GL_TRUE,表示将纹理区域提交到物理内存中:

Evils in the Sparse Texture Memory

在分配之后,glTexSubImage3D和memcpy函数在GPU虚拟内存和CPU虚拟内存之间通过物理内存传输数据。下图中的代码片段是调用glTexSubImage3D函数,将ptr参数设置为image_ptr,表示将纹理数据从CPU内存复制到PMR物理内存:

Evils in the Sparse Texture Memory

当CPU虚拟内存unmap时,会从PMR物理内存取消映射。当TexPageCommitmentEXT的commit参数为GL_FALSE时,将会销毁纹理内存。

Evils in the Sparse Texture Memory

这一切看起来都没什么问题,我们无法重新将销毁的稀疏纹理内存从GPU重新映射到CPU。但如果我们从CPU访问未定义的内存,如下图:

Evils in the Sparse Texture Memory

这会导致一些问题:

1、越界访问
2、引用问题

Evils in the Sparse Texture Memory

3、导致GPU虚拟内存从用户内存开始

Evils in the Sparse Texture Memory

Evils in the Sparse Texture Memory

PowerVR支持不同的page大小,4K, 16K, 64K, 256K, 1M, 2M。利用以上流程,当GPU虚拟内存的page大小与PMR的page大小不同时,将导致OOB漏洞:

Evils in the Sparse Texture Memory


References

[1] https://i.blackhat.com/EU-23/Presentations/EU-23-Jin-Evils-in-the-Sparse-Texture.pdf
[2] https://googleprojectzero.blogspot.com/2022/11/mind-the-gap.html
[3] https://bugs.chromium.org/p/apvi
[4] https://docs.nvidia.com/drive/archive/5.1.6.0L/nvvib_docs/DRIVE_OS_Linux_SDK_Development_Guide/baggage/GL_EXT_sparse_texture.html

原文始发于微信公众号(山石网科安全技术研究院):Evils in the Sparse Texture Memory

版权声明:admin 发表于 2024年2月27日 上午10:44。
转载请注明:Evils in the Sparse Texture Memory | CTF导航

相关文章