ARM MTE简介(超级详细~)

一、MTE介绍(Memory_Tagging_Extension)

MTE 是ARM新架构(ARM V8.5引入)的一个特性,它通过给分配的内存打标记(tag),追踪最常见的非法内存操作。如果密钥的值和锁的值一样,表示访问成功,否则会报告一个错误。

通常内存安全相关的bug,特别是在Android codebase的native代码层是常见的异常类型。按照google的异常统计,超过50%的安全漏洞是内存引起的(如下图所示):


ARM MTE简介(超级详细~)

为了解决和发现内存bug,Android 引入了ASan/HWASan, KASAN, GWP-ASan,KFENCE和MTE内存检测工具,其中MTE是Android 12开始加入支持。

MTE原理和HWASan类似,下面对比分析下:1、检测部分HWASan通过编译方式,在内存访问前插入检测代码;tag(key)生成通过软件随机方式生成;tag(lock)存储在shadow memory中(shadow内存通常提前设定);2、MTE检测和tag生成存储有了硬件层面的支持,MTE通过指令生成tag(key);检测也是通过指令完成;tag(lock)存储的部分存放在物理内存特定区域(无需软件参与显示分配或者设置);重点差异对比表


ARM MTE简介(超级详细~)

以下是三个工具的开销对比,可以直观感受到MTE的提升。


ARM MTE简介(超级详细~)

二、MTE原理

MTE 在实现时就利用的Armv8-A 的TBI(Top Byte Ignore)特性,使用指针的高 4 bits 存储 tag(即 key),使用专用的内存(tagging memory)存储 tag(即 lock)。下图是MTE 工作的原理图


ARM MTE简介(超级详细~)

MTE 引入的新指令操作标记内存:


ARM MTE简介(超级详细~)

大部分指令完成带tag的加减等数据操作,核心指令IRG,STG ,LDG完成关键的tag生成,存储和读取。

打开MTE时,正确的内存访问需要指针的 key 值与该内存的 lock 值保持一致。当 lock 和 key 不匹配时,可选择触发一个同步或异步异常。下面来介绍这两种模式差别:

更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.

ARM MTE简介(超级详细~)

ARM MTE简介(超级详细~)

Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂


1、同步模式(Sync)

Sync 模式是精确的错误检测工具,对性能有一定损耗,在内存访问(ldr/str)时,同步检测tag是否匹配,如果不匹配异常触发,进程会收到SIGSEGV (code SEGV_MTESERR)信号同时立即结束;

其中:siginfo.si_code = SEGV_MTESERR(SERR中的S表示synchronous),siginfo.si_addr = )

当工作在sync 模式时,android 分配器还会记录分配释放的stack trace,出现问题时还能提供更详细的memory异常分类,如 user-after-free,buffer-overflow 等;

2、异步模式(ASync)

更轻量级的内存检测,Android用来发现内存踩踏bug;在内存访问时,异步检测tag是否匹配,如果不匹配则更新TFSR_EL1寄存器中的TF0 bit。当下一次用户/内核切换时,系统会去检测TFSR_EL1寄存器,然后产生SIGSEGV信号(code = SEGV_MTEAERR);此时的 siginfo.si_code = SEGV_MTEAERR (AERR中的A表示asynchronous),siginfo.si_addr = 0 ,表明系统并不知道是哪一条具体的指令导致的问题。

三、MTE在Android S平台使用方法

MTE 检测分为上层native 和kernel 部分,先来看上层native 使能部分

1、使能所有的native code,通过环境变量控制


ARM MTE简介(超级详细~)

2、增加指定路径的project (BoardConfig.mk中)

PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHSPRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS

3、通过Android bp setting打开


ARM MTE简介(超级详细~)

4、在版本中还可以通过属性控制:

arm64.memtag.process. = off| async | sync

比如使能system_server进程mte: arm64.memtag.process.system_server

5、NDK API 控制使能方法

mallopt (NONE or TBI or ASYNC or SYNC)

6、注意默认版本中MTE是关闭的,需要检查对应的rc文件确保MTE打开

[device/XXX/init.target.rc]

on early-init

# export MEMTAG_OPTIONS off

修改成sync 或者async

kernel 侧MTE 使能

1、确认平台本身MTE 已经支持

2、MTE kernel 相关配置

CONFIG_ARM64_MTE // using MTE in the userspace

CONFIG_KASAN and CONFIG_KASAN_HW_TAGS //using MTE in the kernel

3、通过cmdline 控kasan 开关:


ARM MTE简介(超级详细~)

四、MTE在KERNEL和Android S中的实现

1.MTE打开方法

SCTLR_EL1寄存器用来打开关闭MTE,在TCF0设置MTE模式

ARM MTE简介(超级详细~)


ARM MTE简介(超级详细~)

Android 中MTE 打开的流程

ARM MTE简介(超级详细~)

上层scudo malloc分配时流程

ARM MTE简介(超级详细~)

2.MTE工作流程

Tag 检测异常触发流程,data abort时,arm 通过ESR_EL1(Exception SyndromeRegister) 寄存器ISS部分(低22位)来记录:

ARM MTE简介(超级详细~)

DFSC, bits[5:0] Data Fault Status Code. 0b010001 WhenFEAT_MTE2 is implemented Synchronous TagCheck Fault. DFSC为17时表示触发了MTE tag 检查不匹配; SYNC模式工作流程

ARM MTE简介(超级详细~)

ASYNC模式工作流程

ARM MTE简介(超级详细~)

从sync 和async 实现部分看,sync 在触发异常时立刻发送SEGV_MTESERR给进程,而async在进程系统调用或其他user/kernel切换时,主动检查mte 标记位然后发送SEGV_MTEAERR。

3. MTE检测框图

MTE在tag mismatch时触发异常,消息回传用户程序后,抓取当前调用栈和访问内存及附近的tag,通过指针tag和内存tag来判断是哪种内存异常。

ARM MTE简介(超级详细~)


ARM MTE简介(超级详细~)

MTE Kernel 中的实现如下,关键宏定义:

ARM MTE简介(超级详细~)

上面的定义可以明显看到MTE Tag size为 4 ,标记16个字节,tag存放在指针偏移56位;当前kernel中TAG的值范围0xF0~0xFD, 0xFE用来标记未分配或已经释放的内存。

ARM MTE简介(超级详细~)

上图是kernel 触发tag mismatch的原理图,需要注意的是MTE 的tag lock储存读取也是硬 件实现的。

五、MTE 检测案例

1.userspace踩踏例子

Example1:underflow
std::unique_ptr p =std::make_unique(4);
 volatile int oob = p[-1];
(void)oob;
 
执行代码后触发tomstone
ARM MTE简介(超级详细~)


ARM MTE简介(超级详细~)

Example2: overflow

std::unique_ptr p =std::make_unique(4);

 volatile int oob = p[5];
 (void)oob;
执行代码后触发tomstone
ARM MTE简介(超级详细~)

ARM MTE简介(超级详细~)

Example3: useafter free

int * p = new int[16];
delete[] p;
p[3] = 9;
ARM MTE简介(超级详细~)

ARM MTE简介(超级详细~)

上面的例子中可以看到MTE 能准确的检测到常见的uaf, unferflow, overflow 异常; 整体异常

2. Kernel hwkasan例子

Example : kmalloc overflow

ARM MTE简介(超级详细~)

ARM MTE简介(超级详细~)

ARM MTE简介(超级详细~)

测试代码sample: ptr = kmalloc(234, GFP_KERNEL); ptr[256] = 0; 从上面的log看kernel mte 检测过程:分配的指针:fdffff8878a8f500越界的地方:fdffff8878a8f601 Pointer tag : [fd], memory tag: [f4].ptr指针为0xfdffff8878a8f500,标记的可访问内存是0xffffff8878a8f500 ~ 0xffffff8878a8f5EA, mte是标记是16字节对齐的,所以实际是标记连续234/16 =14.6= 15个(向上取整)。Ptr指针只能访问tag为fd标记的区域,实际ptr 访问到了tag为f4标记的内存区域,通过tag计算我们也能推导出内存访问越界点;

ARM MTE简介(超级详细~)

Under flow 流程类似overflow,tag 打印会在ptr能访问内存的前面; User after free 的话mem的tag会变成fe(用来表示已经释放或无法访问的内存);

六、总结

当前MTE 在android中需要绑定scudo 分配器使用,相信随着更多平台支撑mte后,传统的 分配器也会引入mte。 MTE 通过4 bit Tag Size 来做key lock 匹配,存在连续两个内存分配相同的tag key情况, 也就是1/16 概率出现无法检测的情况(kernel 中实际只有14个tag可用,实际概率更高) ;如果能保证相邻内存tag 不同的话,miss的概率会大大降低。 MTE 无法检测 16 byte内的内存越界访问;分配大小非16字节对齐,向后踩踏<16字节 对齐的踩踏无法被MTE检测到,比如: ptr = kmalloc(12, GFP_KERNEL); ptr[12] = 0; …. ptr[15] = 0; ptr[16] = 0; //这里才能触发检测 MTE不依赖编译,低性能损耗,可以考虑将MTE在用户/试用用户侧选择性的打开,发 现内部难以发现的内存类问题。

ARM MTE简介(超级详细~)

简介   详细   ARM
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章