Linux Mem -- 关于AArch64 MTE功能的疑问
目录
1.虚拟地址和物理地址映射完成后,才可以设置虚拟地址对应的memory tag ?
2.各种memory allocator中的address tag从哪来,怎么产生?
2.1 vmalloc allocator
2.2 slub分配器
2.3 用户可以指定IRG指令产生的address tag
3.kasan_unpoison 标记的内存地址需要16byte对齐,该对齐由谁保证?
4.被标记过的内存,释放时会执行哪些特定动作?
前面几篇文章是在介绍为什么需要MTE功能、MTE功能逻辑和硬件实现。从这篇文件起,结合之前MTE逻辑和硬件实现,开始介绍MTE功能在软件中的实现。首先我要通过调查推理回答自己的几个问题。
1.虚拟地址和物理地址映射完成后,才可以设置虚拟地址对应的memory tag ?
Documentation – Arm Developer 根据文章描述和文章中图的示意,memory tag 是与16 字节的物理内存空间是关联的,存放memory tag的tag storage空间大小取决于物理内存空间大小。而且通过调查发现:系统在设置por_mtu_tag_addr_base 寄存器时使用的物理地址。
结合如上推断例如指令 stg x0, [x0] 要设置x0[59 - 56]的address tag到[x0]对应的tag storage空间时,是在x0寄存器中的虚拟地址转换为物理地址后,再找到物理地址在tag storage中对应的位置,最后将address tag做为memory tag存放到该对应空间。
按照如上逻辑:虚拟地址需要完成其和物理地址的映射后才可以设置或则读取其对应的memory tag。
从代码逻辑也可以推断此行为,如下代码逻辑。首先进行虚拟空间分配,在进行虚拟和物理地址空间之间的映射,只有在虚拟和物理地址空间成功建立映射后才会对该内存空间进行unpoison操作(即标记该部分空间已分配)。
void *vm_map_ram(struct page **pages, unsigned int count, int node)
{
unsigned long size = (unsigned long)count << PAGE_SHIFT;
unsigned long addr;
void *mem;
//获取虚拟地址空间
if (likely(count <= VMAP_MAX_ALLOC)) {
mem = vb_alloc(size, GFP_KERNEL);
if (IS_ERR(mem))
return NULL;
addr = (unsigned long)mem;
} else {
struct vmap_area *va;
va = alloc_vmap_area(size, PAGE_SIZE,
VMALLOC_START, VMALLOC_END,
node, GFP_KERNEL, VMAP_RAM,
NULL);
if (IS_ERR(va))
return NULL;
addr = va->va_start;
mem = (void *)addr;
}
//建议虚拟空间和物理内存空间之间的映射
if (vmap_pages_range(addr, addr + size, PAGE_KERNEL,
pages, PAGE_SHIFT) < 0) {
vm_unmap_ram(mem, count);
return NULL;
}
/*
* Mark the pages as accessible, now that they are mapped.
* With hardware tag-based KASAN, marking is skipped for
* non-VM_ALLOC mappings, see __kasan_unpoison_vmalloc().
*/
//MTE使能时,进行tag标记
mem = kasan_unpoison_vmalloc(mem, size, KASAN_VMALLOC_PROT_NORMAL);
return mem;
}
2.各种memory allocator中的address tag从哪来,怎么产生?
2.1 vmalloc allocator
以vmalloc分配器为例,从 __kasan_unpoison_vmalloc 函数可以获知 address tag来自于kasan_random_tag函数,通过IRG指令产生。
2.2 slub分配器
从slab分配器可知,address tag同样来自kasan_random_tag,由IRG指令产生。
2.3 用户可以指定IRG指令产生的address tag
如前文《Linux Mem -- MTE in AArch64 Linux-CSDN博客》介绍用户进程可以通过prctl(PR_SET_TAGGED_ADDR_CTRL, flags, 0, 0, 0)系统函数指定特定的tag值。其内核层相关代码如下:
static void mte_update_gcr_excl(struct task_struct *task)
{
/*
* SYS_GCR_EL1 will be set to current->thread.mte_ctrl value by
* mte_set_user_gcr() in kernel_exit, but only if KASAN is enabled.
*/
if (kasan_hw_tags_enabled())
return;
// thread.mte_ctrl中的tag字段来自于用户程序设定,即prctl(PR_SET_TAGGED_ADDR_CTRL)参数设定
write_sysreg_s(
((task->thread.mte_ctrl >> MTE_CTRL_GCR_USER_EXCL_SHIFT) &
SYS_GCR_EL1_EXCL_MASK) | SYS_GCR_EL1_RRND,
SYS_GCR_EL1);
}
Tag相关寄存器说明:
GCR_EL1 : tag control register , 用于控制IRG(随机tag生成指令)生成tag值。
SYS_GCR_EL1_RRND : GCR_EL1.bit16 = 1 表示使用指定tag值,即IRG生成的tag为特定值
3.kasan_unpoison 标记的内存地址需要16byte对齐,该对齐由谁保证?
如上是slub分配器分配内存的函数片段。MTE使能时,arch_slab_minalign()函数获取到的slub内存分配器最小对齐要求为MTE_GRANULE_SIZE(16字节,MTE对齐粒度)。因为系统内存分配器设置的对齐粒度最小为MTE_GRANULE_SIZE,所以获取到的内存空间起始地址也为MTE_GRANULE_SIZE对齐,故kasan_unpoison函数执行时判读入参object可以满足MTE_GRANULE_SIZE对齐要求。
4.被标记过的内存,释放时会执行哪些特定动作?
如上是slub分配器的free函数调用片段。此处只关注MTE功能的影响,上slub分配器的kfree函数,最终会调用到kasan_slab_free -->poison_slab_object函数。Poision_slab_object函数只会对释放内存正确性和可访问性进行判断,如果正确、可访问则将该内存区域标记为KASAN_SLAB_FREE(0xFE)便于后续该内存的再分配。否则调用kasan_report_invalid_free函数,上报kasan检测异常。