Skip to content

Commit 3d65860

Browse files
davidhildenbrandakpm00
authored andcommitted
drivers/virt/acrn: fix PFNMAP PTE checks in acrn_vm_ram_map()
Patch series "mm: follow_pte() improvements and acrn follow_pte() fixes". Patch #1 fixes a bunch of issues I spotted in the acrn driver. It compiles, that's all I know. I'll appreciate some review and testing from acrn folks. Patch #2+#3 improve follow_pte(), passing a VMA instead of the MM, adding more sanity checks, and improving the documentation. Gave it a quick test on x86-64 using VM_PAT that ends up using follow_pte(). This patch (of 3): We currently miss handling various cases, resulting in a dangerous follow_pte() (previously follow_pfn()) usage. (1) We're not checking PTE write permissions. Maybe we should simply always require pte_write() like we do for pin_user_pages_fast(FOLL_WRITE)? Hard to tell, so let's check for ACRN_MEM_ACCESS_WRITE for now. (2) We're not rejecting refcounted pages. As we are not using MMU notifiers, messing with refcounted pages is dangerous and can result in use-after-free. Let's make sure to reject them. (3) We are only looking at the first PTE of a bigger range. We only lookup a single PTE, but memmap->len may span a larger area. Let's loop over all involved PTEs and make sure the PFN range is actually contiguous. Reject everything else: it couldn't have worked either way, and rather made use access PFNs we shouldn't be accessing. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Fixes: 8a6e85f ("virt: acrn: obtain pa from VMA with PFNMAP flag") Signed-off-by: David Hildenbrand <[email protected]> Cc: Alex Williamson <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Fei Li <[email protected]> Cc: Gerald Schaefer <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Paolo Bonzini <[email protected]> Cc: Yonghua Huang <[email protected]> Cc: Sean Christopherson <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent d4a34d7 commit 3d65860

File tree

1 file changed

+47
-16
lines changed
  • drivers/virt/acrn

1 file changed

+47
-16
lines changed

drivers/virt/acrn/mm.c

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,49 +156,83 @@ int acrn_vm_memseg_unmap(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
156156
int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
157157
{
158158
struct vm_memory_region_batch *regions_info;
159-
int nr_pages, i = 0, order, nr_regions = 0;
159+
int nr_pages, i, order, nr_regions = 0;
160160
struct vm_memory_mapping *region_mapping;
161161
struct vm_memory_region_op *vm_region;
162162
struct page **pages = NULL, *page;
163163
void *remap_vaddr;
164164
int ret, pinned;
165165
u64 user_vm_pa;
166-
unsigned long pfn;
167166
struct vm_area_struct *vma;
168167

169168
if (!vm || !memmap)
170169
return -EINVAL;
171170

171+
/* Get the page number of the map region */
172+
nr_pages = memmap->len >> PAGE_SHIFT;
173+
if (!nr_pages)
174+
return -EINVAL;
175+
172176
mmap_read_lock(current->mm);
173177
vma = vma_lookup(current->mm, memmap->vma_base);
174178
if (vma && ((vma->vm_flags & VM_PFNMAP) != 0)) {
179+
unsigned long start_pfn, cur_pfn;
175180
spinlock_t *ptl;
181+
bool writable;
176182
pte_t *ptep;
177183

178184
if ((memmap->vma_base + memmap->len) > vma->vm_end) {
179185
mmap_read_unlock(current->mm);
180186
return -EINVAL;
181187
}
182188

183-
ret = follow_pte(vma->vm_mm, memmap->vma_base, &ptep, &ptl);
184-
if (ret < 0) {
185-
mmap_read_unlock(current->mm);
189+
for (i = 0; i < nr_pages; i++) {
190+
ret = follow_pte(vma->vm_mm,
191+
memmap->vma_base + i * PAGE_SIZE,
192+
&ptep, &ptl);
193+
if (ret)
194+
break;
195+
196+
cur_pfn = pte_pfn(ptep_get(ptep));
197+
if (i == 0)
198+
start_pfn = cur_pfn;
199+
writable = !!pte_write(ptep_get(ptep));
200+
pte_unmap_unlock(ptep, ptl);
201+
202+
/* Disallow write access if the PTE is not writable. */
203+
if (!writable &&
204+
(memmap->attr & ACRN_MEM_ACCESS_WRITE)) {
205+
ret = -EFAULT;
206+
break;
207+
}
208+
209+
/* Disallow refcounted pages. */
210+
if (pfn_valid(cur_pfn) &&
211+
!PageReserved(pfn_to_page(cur_pfn))) {
212+
ret = -EFAULT;
213+
break;
214+
}
215+
216+
/* Disallow non-contiguous ranges. */
217+
if (cur_pfn != start_pfn + i) {
218+
ret = -EINVAL;
219+
break;
220+
}
221+
}
222+
mmap_read_unlock(current->mm);
223+
224+
if (ret) {
186225
dev_dbg(acrn_dev.this_device,
187226
"Failed to lookup PFN at VMA:%pK.\n", (void *)memmap->vma_base);
188227
return ret;
189228
}
190-
pfn = pte_pfn(ptep_get(ptep));
191-
pte_unmap_unlock(ptep, ptl);
192-
mmap_read_unlock(current->mm);
193229

194230
return acrn_mm_region_add(vm, memmap->user_vm_pa,
195-
PFN_PHYS(pfn), memmap->len,
231+
PFN_PHYS(start_pfn), memmap->len,
196232
ACRN_MEM_TYPE_WB, memmap->attr);
197233
}
198234
mmap_read_unlock(current->mm);
199235

200-
/* Get the page number of the map region */
201-
nr_pages = memmap->len >> PAGE_SHIFT;
202236
pages = vzalloc(array_size(nr_pages, sizeof(*pages)));
203237
if (!pages)
204238
return -ENOMEM;
@@ -242,12 +276,11 @@ int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
242276
mutex_unlock(&vm->regions_mapping_lock);
243277

244278
/* Calculate count of vm_memory_region_op */
245-
while (i < nr_pages) {
279+
for (i = 0; i < nr_pages; i += 1 << order) {
246280
page = pages[i];
247281
VM_BUG_ON_PAGE(PageTail(page), page);
248282
order = compound_order(page);
249283
nr_regions++;
250-
i += 1 << order;
251284
}
252285

253286
/* Prepare the vm_memory_region_batch */
@@ -264,8 +297,7 @@ int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
264297
regions_info->vmid = vm->vmid;
265298
regions_info->regions_gpa = virt_to_phys(vm_region);
266299
user_vm_pa = memmap->user_vm_pa;
267-
i = 0;
268-
while (i < nr_pages) {
300+
for (i = 0; i < nr_pages; i += 1 << order) {
269301
u32 region_size;
270302

271303
page = pages[i];
@@ -281,7 +313,6 @@ int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
281313

282314
vm_region++;
283315
user_vm_pa += region_size;
284-
i += 1 << order;
285316
}
286317

287318
/* Inform the ACRN Hypervisor to set up EPT mappings */

0 commit comments

Comments
 (0)