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 "base/allocator/partition_allocator/pkey.h"
6 #include "base/allocator/partition_allocator/address_pool_manager.h"
7 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
8 #include "base/allocator/partition_allocator/reservation_offset_table.h"
9 
10 #if BUILDFLAG(ENABLE_PKEYS)
11 
12 #include <errno.h>
13 #include <sys/mman.h>
14 #include <sys/syscall.h>
15 #include <unistd.h>
16 
17 #include "base/allocator/partition_allocator/partition_alloc_base/cpu.h"
18 #include "base/allocator/partition_allocator/partition_alloc_check.h"
19 
20 #if !BUILDFLAG(IS_LINUX)
21 #error "This pkey code is currently only supported on Linux"
22 #endif
23 
24 namespace partition_alloc::internal {
25 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)26 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
27 bool CPUHasPkeySupport() {
28   return base::CPU::GetInstanceNoAllocation().has_pku();
29 }
30 
31 PkeySettings PkeySettings::settings PA_PKEY_ALIGN;
32 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)33 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
34 int PkeyMprotect(void* addr, size_t len, int prot, int pkey) {
35   return syscall(SYS_pkey_mprotect, addr, len, prot, pkey);
36 }
37 
PkeyMprotectIfEnabled(void * addr,size_t len,int prot,int pkey)38 int PkeyMprotectIfEnabled(void* addr, size_t len, int prot, int pkey) {
39   if (PA_UNLIKELY(PkeySettings::settings.enabled)) {
40     return PkeyMprotect(addr, len, prot, pkey);
41   }
42 
43   PA_CHECK(pkey == 0);
44 
45   return mprotect(addr, len, prot);
46 }
47 
TagMemoryWithPkey(int pkey,void * address,size_t size)48 void TagMemoryWithPkey(int pkey, void* address, size_t size) {
49   PA_DCHECK(
50       (reinterpret_cast<uintptr_t>(address) & PA_PKEY_ALIGN_OFFSET_MASK) == 0);
51   PA_PCHECK(
52       PkeyMprotect(address,
53                    (size + PA_PKEY_ALIGN_OFFSET_MASK) & PA_PKEY_ALIGN_BASE_MASK,
54                    PROT_READ | PROT_WRITE, pkey) == 0);
55 }
56 
57 template <typename T>
TagVariableWithPkey(int pkey,T & var)58 void TagVariableWithPkey(int pkey, T& var) {
59   TagMemoryWithPkey(pkey, &var, sizeof(T));
60 }
61 
TagGlobalsWithPkey(int pkey)62 void TagGlobalsWithPkey(int pkey) {
63   TagVariableWithPkey(pkey, PartitionAddressSpace::setup_);
64 
65   AddressPoolManager::Pool* pool =
66       AddressPoolManager::GetInstance().GetPool(kPkeyPoolHandle);
67   TagVariableWithPkey(pkey, *pool);
68 
69   uint16_t* pkey_reservation_offset_table =
70       GetReservationOffsetTable(kPkeyPoolHandle);
71   TagMemoryWithPkey(pkey, pkey_reservation_offset_table,
72                     ReservationOffsetTable::kReservationOffsetTableLength);
73 
74   TagVariableWithPkey(pkey, PkeySettings::settings);
75 }
76 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)77 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
78 int PkeyAlloc(int access_rights) {
79   return syscall(SYS_pkey_alloc, 0, access_rights);
80 }
81 
PA_COMPONENT_EXPORT(PARTITION_ALLOC)82 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
83 void PkeyFree(int pkey) {
84   PA_PCHECK(syscall(SYS_pkey_free, pkey) == 0);
85 }
86 
Rdpkru()87 uint32_t Rdpkru() {
88   uint32_t pkru;
89   asm volatile(".byte 0x0f,0x01,0xee\n" : "=a"(pkru) : "c"(0), "d"(0));
90   return pkru;
91 }
92 
Wrpkru(uint32_t pkru)93 void Wrpkru(uint32_t pkru) {
94   asm volatile(".byte 0x0f,0x01,0xef\n" : : "a"(pkru), "c"(0), "d"(0));
95 }
96 
97 #if BUILDFLAG(PA_DCHECK_IS_ON)
98 
LiftPkeyRestrictionsScope()99 LiftPkeyRestrictionsScope::LiftPkeyRestrictionsScope()
100     : saved_pkey_value_(kDefaultPkeyValue) {
101   if (!PkeySettings::settings.enabled) {
102     return;
103   }
104   saved_pkey_value_ = Rdpkru();
105   if (saved_pkey_value_ != kDefaultPkeyValue) {
106     Wrpkru(kAllowAllPkeyValue);
107   }
108 }
109 
~LiftPkeyRestrictionsScope()110 LiftPkeyRestrictionsScope::~LiftPkeyRestrictionsScope() {
111   if (!PkeySettings::settings.enabled) {
112     return;
113   }
114   if (Rdpkru() != saved_pkey_value_) {
115     Wrpkru(saved_pkey_value_);
116   }
117 }
118 
119 #endif  // BUILDFLAG(PA_DCHECK_IS_ON)
120 
121 }  // namespace partition_alloc::internal
122 
123 #endif  // BUILDFLAG(ENABLE_PKEYS)
124