目录
1 介绍
2 威胁模型
3 MTE的内存安全
4 架构细节
5 在ARMv8-A架构,MTE添加了如下指令,可根据策略分为三种:
6 大量部署MTE
7 MTE的硬件层部署
8 MTE的软件层部署
8.1 Heap Tagging
8.2 Stack Tagging
9 MTE优化
近期在深入了解AArch64 Linux MTE机制,本文是个人对于ARM MTE学习。原文来自《Arm_Memory_Tagging_Extension_Whitepaper.pdf》。
1 介绍
内存安全违例主要有两种:空间安全和时间安全。在设计、开发的违规案例的第一阶段是运用恶意炸弹或者是链接和其他有类型的安全隐患去获取系统控制权限或者泄露系统权限信息。
当一个对象访问它的真实边界之外时,就属于空间安全的违例。例如,当栈中的buffer溢出,这中违例可能导致函数的返回地址被重写,这可能导致一种基础的若干类型的攻击。
当一个对象的指针超出其指向的范围时,就会造成时间安全类型的违例。通常在重新分配了支持对象的内存之后。例如,当包含某种函数指针的类型被恶意数据覆盖时,也可能导致一种基础的若干类型的攻击。
MTE提供一种能够检测主要类型的内存安全违规机制。MTE通过提高测试和模糊化的有效性,在部署前帮助检测潜在的漏洞。MTE还可以帮助在部署后大规模检测漏洞。
伴随仔细的软件设计,真实边界内存前后的立即访问引起的序列化安全违例总是能够被检测的。地址空间任意位置的违例可以概率性被检测。
部署前定位和修复安全漏洞能够减少部署代码的攻击面。部署后大规模的检测漏洞可以防止漏洞在被利用前修复。
2 威胁模型
MTE设计为提供数据健壮性,防止有害代码或者攻击者提供的数据。他不会处理算法漏洞或者恶意软件。MTE设计用于检测内存安全违例、增加内存健壮性防止利用内存违例的攻击。在动态链接系统,久代码可以利用MTE进行队内存分配,不需要重新编译代码。
应用MTE到stack需要重新编译代码。MTE结构设计假设堆栈指针非常有价值。当堆栈分配部署MTE时,因此,将MTE的使用与其他特性相结合是很重要的作为分支目标识别(BTI)和指针身份验证码(PAC),以减少允许攻击者控制堆栈指针的小工具存在的概率。
3 MTE的内存安全
ARM的MTE使用lock和key访问内存,Locks可以被设置到内存,key在访问内存是被提供。如果key 和lock 是匹配的,此次内存访问时允许的。如果不匹配,将上报错误。
分配的内存通过每16字节的物理内存添加4bit的元素来进行标记,这是标记的粒度。标记内存实现lock。虚拟地址指针包含key。
为了在不需要请求大量内存指针的情况下实现key-bit,MTE使用ARMv8-A的TBI(top byte Ignore)特性。当TBI使能时,虚拟地址的顶部字节被忽略。因此top字节可以存储metadata。top字节的4bit用于提供mte的key使用。
MTE依赖lock和key的不同来检测内存安全违例。因为可用标签bit数量有限,所以不能保证两个分配的内存有不同的tag。但是,内存分配器可以保证连续分配的内存的tag总是不同的,以此来确保大部分一般的内存违例是可以被检测的。
通常,MTE支持基于seed产生随机tag或者伪随机tag。如果程序执行足够多的次数,则其产生的违例可能被100%发现。
4 架构细节
ARM架构中MTE添加一个新内存类型:常规标记内存。一些例外,可以静态的确定访问安全,加载和存储方式访问已标记的内存时,虚拟地址的top字节将和存储在内存中tag进行比较。当地址中的tag和内存中的tag不一致时,可以配置触发系统同步异常或者发出异步报告。
当配置不一致时触发异步报告,异常细节被保存在系统寄存器,系统运行在更高异常级,来确保这些寄存器被更新。这种方式可以使操作系统隔离这些异常到运行的部分线程,然后基于这些异常信息在做决定。
同步异常可以精准的,可以准确的是加载还是存储指令引发的不一致。相反的,异步报告不沟准确。异步报告仅仅可以隔离不一致到部分运行的线程。
5 在ARMv8-A架构,MTE添加了如下指令,可根据策略分为三种:
略
6 大量部署MTE
ARM认为MTE可以用于不同的配置策略到各产品开发、部分各阶段。
精准检测的目是为了得到更多的失败信息,非精准检测目的在于保证更高的性能。
系统内核可决定是否终止引起tag不一致异常的进程或者仅收集异常信息、允许进程继续执行。测试使能MTE的产品可以找到更多的潜在的问题。这个阶段更适合于检测并记录尽可能多的异常信息,系统不必针对攻击做保护。推荐系统配置为:
a.执行精准检测
b.收集引起不一致的数据而不是终止进程
这种配置可以通过测试和模糊检测找大量的违例、收集更多的信息。
产品发布后,推荐如下方式配置MTE:
a.执行非精准检测
b.终止引起不一致的tag的进程
这种配资提供性能和内存安全违例检测的平衡。发布后,这种配置可以应用于对黑客具有高价值的进程。例如,对于加密key的存储进程执行精准检测,以至于关于此异常的更多信息能够提供给相应的开发者。
也可以针对系统改变适应的MTE配置。例如,使用非精准检测的内存进程因tag不一致被终止运行,该进程下次运行时使用精准检测配置去收集更多的检测信息提供给开发者。这种混合的部署模型即兼顾非精准检测的性能又可以通过精准检测提供更多高质量反馈信息。
7 MTE的硬件层部署
为了让ARM产品支持MTE功能,一个新版本AMBA5 CHI(Coherent Hub Interface)正被开发,用于支持MTE的传输一致性需求。
8 MTE的软件层部署
MTE设置支持多种级别的部署。
8.1 Heap Tagging
在动态链接系统中,可以部署tagged堆而不需要次修改已存在的二进制库。仅仅改变系统内核和C库代码。
Linux内核要支持ARM架构的MTE,如下区域需要被改变:
a.当使用地址空间管理时,需要有能力移除用户空间指针中的tag
b.在虚拟内存系统中使用clear_page和copy_page函数时需要注意tag
c.添加tag不一致引起异常的处理,类似于传输失效时发送SIGSEGV信号。
d.转换内存映射,提供给用户进程的内存空间需要使用tagged内存。
e.添加扩展的检测和系统寄存器配置
C库中,ARM修改如下内存相关函数:
malloc / free/ calloc / realloc
总之,内存拷贝和字串相关函数被需改用于防止这些函数越界访问原始buffer。
8.2 Stack Tagging
标记运行的栈需要编译和内核支持,原有二进制需要重新编译。栈标记多有多种不同策略。
我们的合作伙伴制定了选择随机标签的策略,使用IRG指令在函数入口一个新的栈帧被分配时,然后编译器使用ADDG、SUBG指令创建标记地址为每个带有此函数的栈点,该tag来自于初始的随机tag值。分配的栈空间可以使用推荐的tag存储指令进行批量的初始化,但是在使用该函数前编译器不需要初始化任何栈点。
该策略保证了MTE的统计策略是有效的对于函数调用,确保栈中临近的对象有不用tag,因此顺序性栈上溢或者下溢可以被检测到。
保护栈空间相邻对象需要改对象是16字节的tag粒度对齐,所以开启MTE功能后会导致栈空间增加。我们的基准测试表明栈空间增幅较小。
为了增加性能,MTE不会检查基于栈指针的立即数偏移形式访问内存。这是因为编译器能够静态的证明这些错误或者是在编译时发出错误诊断。
9 MTE优化
MTE不需要修改源码来纠正代码,然而当tag抓取或者储存到系统内存时,MTE必然会增加负载。这些负载与分配内存的大小、生命周期、tag和数据的集合或分离等有关。负载能够最小化通过如下方式:
a.同时写tag并初始化内存
在许多情况下,内存必须初始化为零、在进行tag设置。例如,在内存提供该用户空间前系统内核需要清除内存页。基于Linux的ARM使用STGM指令实现此目的
b.避免分配过量的从来不会写入的内存空间
一些情况下,软件可能分配远超自身需要的内存空间、而仅使用其中的部分。这种情况下使用MTE是非常浪费的,因为数据不会被写入内存但是任然要写入tag。
c.避免过多的释放和再分配
即使MTE未部署情况下,避免过多的释放和再分配也是好的建议。然而,使能MTE后固定分配和释放的代价上升,性能问题可能被放大。
d.避免从栈中分配较大的固定大小内存
在栈中分配的较大的固定大小内存往往没有被完全使用,例如,PATH_MAX固定size的buffer经常仅包含相对较短的字串。避免这些内存分配降低栈空间的负载进而减少不必要tag写入。