1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "partition_alloc/thread_isolation/pkey.h"
6 
7 #if BUILDFLAG(ENABLE_PKEYS)
8 
9 #include <sys/mman.h>
10 #include <sys/syscall.h>
11 #include <unistd.h>
12 
13 #include <cerrno>
14 
15 #include "partition_alloc/partition_alloc_base/cpu.h"
16 #include "partition_alloc/partition_alloc_check.h"
17 #include "partition_alloc/thread_isolation/thread_isolation.h"
18 
19 #if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
20 #error "This pkey code is currently only supported on Linux and ChromeOS"
21 #endif
22 
23 namespace partition_alloc::internal {
24 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)25 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
26 bool CPUHasPkeySupport() {
27   return base::CPU::GetInstanceNoAllocation().has_pku();
28 }
29 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)30 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
31 int PkeyMprotect(void* addr, size_t len, int prot, int pkey) {
32   return syscall(SYS_pkey_mprotect, addr, len, prot, pkey);
33 }
34 
TagMemoryWithPkey(int pkey,void * address,size_t size)35 void TagMemoryWithPkey(int pkey, void* address, size_t size) {
36   PA_DCHECK((reinterpret_cast<uintptr_t>(address) &
37              PA_THREAD_ISOLATED_ALIGN_OFFSET_MASK) == 0);
38   PA_PCHECK(PkeyMprotect(address,
39                          (size + PA_THREAD_ISOLATED_ALIGN_OFFSET_MASK) &
40                              PA_THREAD_ISOLATED_ALIGN_BASE_MASK,
41                          PROT_READ | PROT_WRITE, pkey) == 0);
42 }
43 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)44 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
45 int PkeyAlloc(int access_rights) {
46   return syscall(SYS_pkey_alloc, 0, access_rights);
47 }
48 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)49 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
50 void PkeyFree(int pkey) {
51   PA_PCHECK(syscall(SYS_pkey_free, pkey) == 0);
52 }
53 
Rdpkru()54 uint32_t Rdpkru() {
55   uint32_t pkru;
56   asm volatile(".byte 0x0f,0x01,0xee\n" : "=a"(pkru) : "c"(0), "d"(0));
57   return pkru;
58 }
59 
Wrpkru(uint32_t pkru)60 void Wrpkru(uint32_t pkru) {
61   asm volatile(".byte 0x0f,0x01,0xef\n" : : "a"(pkru), "c"(0), "d"(0));
62 }
63 
64 #if BUILDFLAG(PA_DCHECK_IS_ON)
65 
LiftPkeyRestrictionsScope()66 LiftPkeyRestrictionsScope::LiftPkeyRestrictionsScope()
67     : saved_pkey_value_(kDefaultPkeyValue) {
68   if (!ThreadIsolationSettings::settings.enabled) {
69     return;
70   }
71   saved_pkey_value_ = Rdpkru();
72   if (saved_pkey_value_ != kDefaultPkeyValue) {
73     Wrpkru(kAllowAllPkeyValue);
74   }
75 }
76 
~LiftPkeyRestrictionsScope()77 LiftPkeyRestrictionsScope::~LiftPkeyRestrictionsScope() {
78   if (!ThreadIsolationSettings::settings.enabled) {
79     return;
80   }
81   if (Rdpkru() != saved_pkey_value_) {
82     Wrpkru(saved_pkey_value_);
83   }
84 }
85 
86 #endif  // BUILDFLAG(PA_DCHECK_IS_ON)
87 
88 }  // namespace partition_alloc::internal
89 
90 #endif  // BUILDFLAG(ENABLE_PKEYS)
91