1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016-20 Intel Corporation. */
#include <linux/lockdep.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/shmem_fs.h>
#include <linux/suspend.h>
#include <linux/sched/mm.h>
#include "arch.h"
#include "encl.h"
#include "encls.h"
#include "sgx.h"
static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
unsigned long addr,
unsigned long vm_flags)
{
unsigned long vm_prot_bits = vm_flags & (VM_READ | VM_WRITE | VM_EXEC);
struct sgx_encl_page *entry;
entry = xa_load(&encl->page_array, PFN_DOWN(addr));
if (!entry)
return ERR_PTR(-EFAULT);
/*
* Verify that the faulted page has equal or higher build time
* permissions than the VMA permissions (i.e. the subset of {VM_READ,
* VM_WRITE, VM_EXECUTE} in vma->vm_flags).
*/
if ((entry->vm_max_prot_bits & vm_prot_bits) != vm_prot_bits)
return ERR_PTR(-EFAULT);
/* No page found. */
if (!entry->epc_page)
return ERR_PTR(-EFAULT);
/* Entry successfully located. */
return entry;
}
static vm_fault_t sgx_vma_fault(struct vm_fault *vmf)
{
unsigned long addr = (unsigned long)vmf->address;
struct vm_area_struct *vma = vmf->vma;
struct sgx_encl_page *entry;
unsigned long phys_addr;
struct sgx_encl *encl;
unsigned long pfn;
vm_fault_t ret;
encl = vma->vm_private_data;
mutex_lock(&encl->lock);
entry = sgx_encl_load_page(encl, addr, vma->vm_flags);
if (IS_ERR(entry)) {
mutex_unlock(&encl->lock);
return VM_FAULT_SIGBUS;
}
phys_addr = sgx_get_epc_phys_addr(entry->epc_page);
/* Check if another thread got here first to insert the PTE. */
if (!follow_pfn(vma, addr, &pfn)) {
mutex_unlock(&encl->lock);
return VM_FAULT_NOPAGE;
}
ret = vmf_insert_pfn(vma, addr, PFN_DOWN(phys_addr));
if (ret != VM_FAULT_NOPAGE) {
mutex_unlock(&encl->lock);
return VM_FAULT_SIGBUS;
}
mutex_unlock(&encl->lock);
return VM_FAULT_NOPAGE;
}
/**
* sgx_encl_may_map() - Check if a requested VMA mapping is allowed
* @encl: an enclave pointer
* @start: lower bound of the address range, inclusive
* @end: upper bound of the address range, exclusive
* @vm_flags: VMA flags
*
* Iterate through the enclave pages contained within [@start, @end) to verify
* that the permissions requested by a subset of {VM_READ, VM_WRITE, VM_EXEC}
* do not contain any permissions that are not contained in the build time
* permissions of any of the enclave pages within the given address range.
*
* An enclave creator must declare the strongest permissions that will be
* needed for each enclave page. This ensures that mappings have the identical
* or weaker permissions than the earlier declared permissions.
*
* Return: 0 on success, -EACCES otherwise
*/
int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start,
unsigned long end, unsigned long vm_flags)
{
unsigned long vm_prot_bits = vm_flags & (VM_READ | VM_WRITE | VM_EXEC);
struct sgx_encl_page *page;
unsigned long count = 0;
int ret = 0;
XA_STATE(xas, &encl->page_array, PFN_DOWN(start));
/*
* Disallow READ_IMPLIES_EXEC tasks as their VMA permissions might
* conflict with the enclave page permissions.
*/
if (current->personality & READ_IMPLIES_EXEC)
return -EACCES;
mutex_lock(&encl->lock);
xas_lock(&xas);
xas_for_each(&xas, page, PFN_DOWN(end - 1)) {
if (~page->vm_max_prot_bits & vm_prot_bits) {
ret = -EACCES;
break;
}
/* Reschedule on every XA_CHECK_SCHED iteration. */
if (!(++count % XA_CHECK_SCHED)) {
xas_pause(&xas);
xas_unlock(&xas);
mutex_unlock(&encl->lock);
cond_resched();
mutex_lock(&encl->lock);
xas_lock(&xas);
}
}
xas_unlock(&xas);
mutex_unlock(&encl->lock);
return ret;
}
static int sgx_vma_mprotect(struct vm_area_struct *vma, unsigned long start,
unsigned long end, unsigned long newflags)
{
return sgx_encl_may_map(vma->vm_private_data, start, end, newflags);
}
const struct vm_operations_struct sgx_vm_ops = {
.fault = sgx_vma_fault,
.mprotect = sgx_vma_mprotect,
};
|