Android 11中对于64位应用arm执行MTE导致crash的问题
Posted 木大白易
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 11中对于64位应用arm执行MTE导致crash的问题相关的知识,希望对你有一定的参考价值。
前言
App 64位包在android 11上crash,崩溃堆栈也很诡异,报了一个native的空指针,代码中一个函数参数为int64_t
类型的指针,并且确定不为空!
分析
在仔细调试时,发现函数中传入的指针地址,和使用时的指针地址不一致,从而导致空指针,但是奇怪的是32位的包就没有这个问题!
在查阅了一些资料,发现是由于Android 11中对于64位的进程,arm v9 cpu启用了MTE( Memory Tagging Extension,内存标记拓展)功能。MTE 的工作原理是对堆栈、堆和全局变量上的每次内存分配的第 56 到 59 个地址位加标记,该标记在具有对 ARM Top-byte Ignore (TBI,顶部字节忽略) 的内核支持的设备上的指针顶部字节中设置。。硬件和指令集会自动检查每次访问内存时是否使用了正确的标记。
在指针顶部字节中错误存储信息的 Android 应用一定会在启用了 MTE 的设备上中断。利用加标记的指针,可以在 MTE 设备可用之前更轻松地检测并拒绝对指针顶部字节的错误使用。
详情可以参考:https://source.android.google.cn/devices/tech/debug/tagged-pointers?hl=zh-cn
另外,里边也提到了什么情况下会导致出现这种问题:
如果应用崩溃,并且您收到包含此链接的提示,这可能意味着存在以下某种情况:
- 应用尝试释放系统堆分配器未分配的指针。
- 应用中的某个部分修改了指针的顶部字节。不能修改指针的顶部字节,您需要更改代码来修复此问题。
指针的顶部字节被错误使用或修改的示例包括:
- 指向特定类型的指针将特定于应用的元数据存储在前 16 个地址位中。
- 指针的类型转换为双精度,然后又恢复为原来的类型,因此丢失了较低的地址位。
- 代码计算不同堆栈帧的局部变量地址之间的差异,作为测量递归深度的方法。
所以如果出现了这种类似的错误,上面列举的这几种情况要额外注意!
关于TBI
最高字节忽略 (TBI) 是 ARMv8 引入的一项功能,它通过忽略虚拟地址的最高有效 8 位来提供内存标记功能。
尽管 64 位 ARM 寄存器是 64 位宽的,但虚拟地址要求高 16 位必须是 0x0000 或 0xFFFF。 TBI 是一种硬件功能,由 AArch64 引入,它允许软件使用 64 位指针的 8 个最高有效位作为标记。 这是通过在翻译控制寄存器(Translation Control Register)中启用内存标记支持来完成的。 启用后,虚拟地址的前(高)八位 (即63~56位) 将被忽略。
解决
方案一(不推荐)
上边文档中给了一个临时的过渡方案:
<application android:allowNativeHeapPointerTagging="false">
...
</application>
方案二
上边的方案虽然不会再报错,但是既然mte已经给你报了内存使用的错误,那么就需要你去检查一下自己的代码,是否出现上述的可能情况。
发现上边传入的int64_t指针,在一个函数内部会转成double,然后获取的时候又转回了int64_t,由于存在精度的丢失,导致前后的地址不一致。这也验证了前边列的一条:指针的类型转换为双精度,然后又恢复为原来的类型,因此丢失了较低的地址位。
参考:
https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/enhancing-memory-safety
https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/enhanced-security-through-mte
以上是关于Android 11中对于64位应用arm执行MTE导致crash的问题的主要内容,如果未能解决你的问题,请参考以下文章
Android中Native ELF的反汇编与破解的一些经验