Back to home page

OSCL-LXR

 
 

    


0001 .. SPDX-License-Identifier: GPL-2.0
0002 .. include:: ../disclaimer-zh_CN.rst
0003 
0004 :Original: Documentation/core-api/protection-keys.rst
0005 
0006 :翻译:
0007 
0008  司延腾 Yanteng Si <siyanteng@loongson.cn>
0009 
0010 :校译:
0011 
0012  吴想成 Wu XiangCheng <bobwxc@email.cn>
0013 
0014 .. _cn_core-api_protection-keys:
0015 
0016 ============
0017 内存保护密钥
0018 ============
0019 
0020 用户空间的内存保护密钥(Memory Protection Keys for Userspace,PKU,亦
0021 即PKEYs)是英特尔Skylake(及以后)“可扩展处理器”服务器CPU上的一项功能。
0022 它将在未来的非服务器英特尔处理器和未来的AMD处理器中可用。
0023 
0024 对于任何希望测试或使用该功能的人来说,它在亚马逊的EC2 C5实例中是可用的,
0025 并且已知可以在那里使用Ubuntu 17.04镜像运行。
0026 
0027 内存保护密钥提供了一种机制来执行基于页面的保护,但在应用程序改变保护域
0028 时不需要修改页表。它的工作原理是在每个页表项中为“保护密钥”分配4个以
0029 前被忽略的位,从而提供16个可能的密钥。
0030 
0031 还有一个新的用户可访问寄存器(PKRU),为每个密钥提供两个单独的位(访
0032 问禁止和写入禁止)。作为一个CPU寄存器,PKRU在本质上是线程本地的,可能
0033 会给每个线程提供一套不同于其他线程的保护措施。
0034 
0035 有两条新指令(RDPKRU/WRPKRU)用于读取和写入新的寄存器。该功能仅在64位
0036 模式下可用,尽管物理地址扩展页表中理论上有空间。这些权限只在数据访问上
0037 强制执行,对指令获取没有影响。
0038 
0039 
0040 系统调用
0041 ========
0042 
0043 有3个系统调用可以直接与pkeys进行交互::
0044 
0045         int pkey_alloc(unsigned long flags, unsigned long init_access_rights)
0046         int pkey_free(int pkey);
0047         int pkey_mprotect(unsigned long start, size_t len,
0048                           unsigned long prot, int pkey);
0049 
0050 在使用一个pkey之前,必须先用pkey_alloc()分配它。一个应用程序直接调用
0051 WRPKRU指令,以改变一个密钥覆盖的内存的访问权限。在这个例子中,WRPKRU
0052 被一个叫做pkey_set()的C函数所封装::
0053 
0054         int real_prot = PROT_READ|PROT_WRITE;
0055         pkey = pkey_alloc(0, PKEY_DISABLE_WRITE);
0056         ptr = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
0057         ret = pkey_mprotect(ptr, PAGE_SIZE, real_prot, pkey);
0058         ... application runs here
0059 
0060 现在,如果应用程序需要更新'ptr'处的数据,它可以获得访问权,进行更新,
0061 然后取消其写访问权::
0062 
0063         pkey_set(pkey, 0); // clear PKEY_DISABLE_WRITE
0064         *ptr = foo; // assign something
0065         pkey_set(pkey, PKEY_DISABLE_WRITE); // set PKEY_DISABLE_WRITE again
0066 
0067 现在,当它释放内存时,它也将释放pkey,因为它不再被使用了::
0068 
0069         munmap(ptr, PAGE_SIZE);
0070         pkey_free(pkey);
0071 
0072 .. note:: pkey_set()是RDPKRU和WRPKRU指令的一个封装器。在tools/testing/selftests/x86/protection_keys.c中可以找到一个实现实例。
0073           tools/testing/selftests/x86/protection_keys.c.
0074 
0075 行为
0076 ====
0077 
0078 内核试图使保护密钥与普通的mprotect()的行为一致。例如,如果你这样做::
0079 
0080         mprotect(ptr, size, PROT_NONE);
0081         something(ptr);
0082 
0083 这样做的时候,你可以期待保护密钥的相同效果::
0084 
0085         pkey = pkey_alloc(0, PKEY_DISABLE_WRITE | PKEY_DISABLE_READ);
0086         pkey_mprotect(ptr, size, PROT_READ|PROT_WRITE, pkey);
0087         something(ptr);
0088 
0089 无论something()是否是对'ptr'的直接访问,这都应该为真。
0090 如::
0091 
0092         *ptr = foo;
0093 
0094 或者当内核代表应用程序进行访问时,比如read()::
0095 
0096         read(fd, ptr, 1);
0097 
0098 在这两种情况下,内核都会发送一个SIGSEGV,但当违反保护密钥时,si_code
0099 将被设置为SEGV_PKERR,而当违反普通的mprotect()权限时,则是SEGV_ACCERR。