22

修志龙_ZenonXiu · 2022年08月23日 · 上海市

Linux Kernel MTE相关文档

Linux kernel也提供了有用的文档。
https://www.kernel.org/doc/ht...

用户空间支持
当CONFIG_ARM64_MTE使能而且CPU硬件也支持MTE时,Linux通过HWCAP2_MTE属性告诉Usersapce kernel支持MTE功能。

PROT_MTE
在访问tags时,用户进程必须为地址范围使能Tagged内存属性,这可以通过为mmap和mprotect系统调用加上一个新的prot flag。

PROT_MTE -- 这个属性表示允许有这个属性的页分配tags

这些页在第一次map到用户空间时,tag被设为0,在做copy-on-write的时候被保留。可以支持MAP_SHARED,允许tag在多个进程间共享。

注意:PROT_MTE 仅支持MAP——ANONYMOUS和RAM-base的文件map(tmfs, memfd)..如果把PROT_MTE传给其他类型,系统调用会返回-EINVAL。

注意:RROT_MTE flag不能被吗protect清除。

注意:madvise带MADV_DONTNEED的内存范围和MAVD_FREE可能在系统调用或的任何地方有为0的tag。

Tag 检查错误
当PROT_MTE在一段地址空间使能,并且出现了地址tag和内存tag的不匹配,那么可以配置成3中行为:

  1. 忽略—这是默认的模式。CPU和内核忽略tag检查错误
  2. 同步-内核同步发送带SEGV_MTESERR码的SIGSEGV,内存访问将不会进行。如果出现错误的线程SIGSEGV被忽略或阻塞,那么线程对应的进程被终结并使用coredump.
  3. 异步-内核发送SIGSEGV,异步地以下一种或多种检查错误,带SEGV_MTEAERR并且。si_addr=0 表示错误地址未知。

用户可以为每个线程选择以上模式,通过使用prctl(PR_SET_TAGGED_ADDR_CTRL, flags, 0, 0, 0)系统调用,其中flags包含了以下在PR_MTE_TCF_MASK位域的值。

  • PR_MTE_TCF_NONE -忽略模式
  • PR_MTE_TCF_SYNC- 同步模式
  • PR_MTE_TCF_ASYNC -异步模式

当前使用的模式可以通过prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0) 系统调用得到。
一个用户线程可以通过MSR TCO,#1指令设置PSTATE.TCO bit来关闭tag检查功能。

注意:内核通过如read()系统调用方式访问用户地址空间时,如果这个线程的check模式为忽略和异步时,不做tag检查。如果模式是同步模式,内核尽量检查tag,但不是不能保证一定检查。内核访问用户地址时,不管用户空间怎么配置,它总是把PSTATE.TCO当0来处理。

为IRG, ADDG, SUBG指令排除一些Tag在外
构架上允许通过设置GCR_EL1.Exclude 将一些tag值排除在tag随机生成的值之外。默认情况下,Linux排除除0之外所有的tag值。用户线程可以通过prctl(PR_SET_TAGGED_ADDR_CTRL, flags, 0, 0, 0)系统调用和设置flag中包含tag的位图在PR_MTE_TAG_MASK来使能特定的tag值。

进程的初始状态
在execve()启动一个进程时,这个新进程有以下配置。

  • PR_TAGGED_ADDR_ENABLE为0,(不使能)
  • 忽略模式
  • PR_MTE_TAG_MASK=0(所有的tag被排除在外)
  • PSTATE.TCO为0
  • 没有任何初始的内存映射带PROT_MTE属性

在fork()时,新进程继承了父进程的配置和内存映射,除了带MADV_WIPEONFORK的madvise()地址范围,它的数据和tag都被清0.

正确使用的例子

* To be compiled with -march=armv8.5-a+memtag
 */
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/prctl.h>

/*
 * From arch/arm64/include/uapi/asm/hwcap.h
 */
#define HWCAP2_MTE              (1 << 18)

/*
 * From arch/arm64/include/uapi/asm/mman.h
 */
#define PROT_MTE                 0x20

/*
 * From include/uapi/linux/prctl.h
 */
#define PR_SET_TAGGED_ADDR_CTRL 55
#define PR_GET_TAGGED_ADDR_CTRL 56
# define PR_TAGGED_ADDR_ENABLE  (1UL << 0)
# define PR_MTE_TCF_SHIFT       1
# define PR_MTE_TCF_NONE        (0UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TCF_SYNC        (1UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TCF_ASYNC       (2UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TCF_MASK        (3UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TAG_SHIFT       3
# define PR_MTE_TAG_MASK        (0xffffUL << PR_MTE_TAG_SHIFT)

/*
 * Insert a random logical tag into the given pointer.
 */
#define insert_random_tag(ptr) ({                       \
        uint64_t __val;                                 \
        asm("irg %0, %1" : "=r" (__val) : "r" (ptr));   \
        __val;                                          \
})

/*
 * Set the allocation tag on the destination address.
 */
#define set_tag(tagged_addr) do {                                      \
        asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \
} while (0)

int main()
{
        unsigned char *a;
        unsigned long page_sz = sysconf(_SC_PAGESIZE);
        unsigned long hwcap2 = getauxval(AT_HWCAP2);

        /* check if MTE is present */
        if (!(hwcap2 & HWCAP2_MTE))
                return EXIT_FAILURE;

        /*
         * Enable the tagged address ABI, synchronous MTE tag check faults and
         * allow all non-zero tags in the randomly generated set.
         */
        if (prctl(PR_SET_TAGGED_ADDR_CTRL,
                  PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT),
                  0, 0, 0)) {
                perror("prctl() failed");
                return EXIT_FAILURE;
        }

        a = mmap(0, page_sz, PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (a == MAP_FAILED) {
                perror("mmap() failed");
                return EXIT_FAILURE;
        }

        /*
         * Enable MTE on the above anonymous mmap. The flag could be passed
         * directly to mmap() and skip this step.
         */
        if (mprotect(a, page_sz, PROT_READ | PROT_WRITE | PROT_MTE)) {
                perror("mprotect() failed");
                return EXIT_FAILURE;
        }

        /* access with the default tag (0) */
        a[0] = 1;
        a[1] = 2;

        printf("a[0] = %hhu a[1] = %hhu\n", a[0], a[1]);

        /* set the logical and allocation tags */
        a = (unsigned char *)insert_random_tag(a);
        set_tag(a);

        printf("%p\n", a);

        /* non-zero tag access */
        a[0] = 3;
        printf("a[0] = %hhu a[1] = %hhu\n", a[0], a[1]);

        /*
         * If MTE is enabled correctly the next instruction will generate an
         * exception.
         */
        printf("Expecting SIGSEGV...\n");
        a[16] = 0xdd;

        /* this should not be printed in the PR_MTE_TCF_SYNC mode */
        printf("...haven't got one\n");

        return EXIT_FAILURE;
}
推荐阅读
关注数
8629
内容数
51
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息