1 .. SPDX-License-Identifier: GPL-2.0 2 .. include:: ../disclaimer-zh_CN.rst 3 4 :Original: Documentation/core-api/protection-keys.rst 5 6 :翻译: 7 8 司延腾 Yanteng Si <siyanteng@loongson.cn> 9 10 :校译: 11 12 吴想成 Wu XiangCheng <bobwxc@email.cn> 13 14 .. _cn_core-api_protection-keys: 15 16 ============ 17 内存保护密钥 18 ============ 19 20 用户空间的内存保护密钥(Memory Protection Keys for Userspace,PKU,亦 21 即PKEYs)是英特尔Skylake(及以后)“可扩展处理器”服务器CPU上的一项功能。 22 它将在未来的非服务器英特尔处理器和未来的AMD处理器中可用。 23 24 对于任何希望测试或使用该功能的人来说,它在亚马逊的EC2 C5实例中是可用的, 25 并且已知可以在那里使用Ubuntu 17.04镜像运行。 26 27 内存保护密钥提供了一种机制来执行基于页面的保护,但在应用程序改变保护域 28 时不需要修改页表。它的工作原理是在每个页表项中为“保护密钥”分配4个以 29 前被忽略的位,从而提供16个可能的密钥。 30 31 还有一个新的用户可访问寄存器(PKRU),为每个密钥提供两个单独的位(访 32 问禁止和写入禁止)。作为一个CPU寄存器,PKRU在本质上是线程本地的,可能 33 会给每个线程提供一套不同于其他线程的保护措施。 34 35 有两条新指令(RDPKRU/WRPKRU)用于读取和写入新的寄存器。该功能仅在64位 36 模式下可用,尽管物理地址扩展页表中理论上有空间。这些权限只在数据访问上 37 强制执行,对指令获取没有影响。 38 39 40 系统调用 41 ======== 42 43 有3个系统调用可以直接与pkeys进行交互:: 44 45 int pkey_alloc(unsigned long flags, unsigned long init_access_rights) 46 int pkey_free(int pkey); 47 int pkey_mprotect(unsigned long start, size_t len, 48 unsigned long prot, int pkey); 49 50 在使用一个pkey之前,必须先用pkey_alloc()分配它。一个应用程序直接调用 51 WRPKRU指令,以改变一个密钥覆盖的内存的访问权限。在这个例子中,WRPKRU 52 被一个叫做pkey_set()的C函数所封装:: 53 54 int real_prot = PROT_READ|PROT_WRITE; 55 pkey = pkey_alloc(0, PKEY_DISABLE_WRITE); 56 ptr = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 57 ret = pkey_mprotect(ptr, PAGE_SIZE, real_prot, pkey); 58 ... application runs here 59 60 现在,如果应用程序需要更新'ptr'处的数据,它可以获得访问权,进行更新, 61 然后取消其写访问权:: 62 63 pkey_set(pkey, 0); // clear PKEY_DISABLE_WRITE 64 *ptr = foo; // assign something 65 pkey_set(pkey, PKEY_DISABLE_WRITE); // set PKEY_DISABLE_WRITE again 66 67 现在,当它释放内存时,它也将释放pkey,因为它不再被使用了:: 68 69 munmap(ptr, PAGE_SIZE); 70 pkey_free(pkey); 71 72 .. note:: pkey_set()是RDPKRU和WRPKRU指令的一个封装器。在tools/testing/selftests/x86/protection_keys.c中可以找到一个实现实例。 73 tools/testing/selftests/x86/protection_keys.c. 74 75 行为 76 ==== 77 78 内核试图使保护密钥与普通的mprotect()的行为一致。例如,如果你这样做:: 79 80 mprotect(ptr, size, PROT_NONE); 81 something(ptr); 82 83 这样做的时候,你可以期待保护密钥的相同效果:: 84 85 pkey = pkey_alloc(0, PKEY_DISABLE_WRITE | PKEY_DISABLE_READ); 86 pkey_mprotect(ptr, size, PROT_READ|PROT_WRITE, pkey); 87 something(ptr); 88 89 无论something()是否是对'ptr'的直接访问,这都应该为真。 90 如:: 91 92 *ptr = foo; 93 94 或者当内核代表应用程序进行访问时,比如read():: 95 96 read(fd, ptr, 1); 97 98 在这两种情况下,内核都会发送一个SIGSEGV,但当违反保护密钥时,si_code 99 将被设置为SEGV_PKERR,而当违反普通的mprotect()权限时,则是SEGV_ACCERR。
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.