vm_fault_hold_user_pages will not return if an address in the range passed in is mapped RO

but an RW mapping exists for the underlying page. This change fixes the bug by using the
page / NULL returned from pmap_extract_and_hold to determine whether or not vm_fault needs
to be called.

The bug was pointed out by alc.

MFC after:	3 days
This commit is contained in:
Kip Macy 2008-09-29 22:13:29 +00:00
parent 2025d69ba7
commit e2b2d0e9d5

View File

@ -65,9 +65,7 @@ __FBSDID("$FreeBSD$");
int
vm_fault_hold_user_pages(vm_offset_t addr, vm_page_t *mp, int count, int flags)
{
vm_offset_t end, va;
vm_paddr_t pa;
int faults, rv;
struct thread *td;
@ -96,7 +94,6 @@ vm_fault_hold_user_pages(vm_offset_t addr, vm_page_t *mp, int count, int flags)
prot = VM_PROT_READ;
prot |= (flags & VM_HOLD_WRITEABLE) ? VM_PROT_WRITE : 0;
bzero(pages, sizeof(vm_page_t *) * count);
retry:
/*
@ -115,13 +112,12 @@ retry:
* we were only acquiring the pmap lock 1 time as opposed to potentially
* many dozens of times
*/
m = pmap_extract_and_hold(pmap, va, prot);
*pages = m = pmap_extract_and_hold(pmap, va, prot);
if (m == NULL) {
faults++;
continue;
}
*pages = m;
if (flags & VM_HOLD_WRITEABLE)
vm_page_dirty(m);
}
@ -137,16 +133,14 @@ retry:
* trigger a fault where neccessary
*
*/
for (va = addr; va < end; va += PAGE_SIZE) {
m = NULL;
pa = pmap_extract(pmap, va);
for (pages = mp, va = addr; va < end; va += PAGE_SIZE, pages++) {
m = *pages;
rv = 0;
if (pa)
m = PHYS_TO_VM_PAGE(pa);
if (flags & VM_HOLD_WRITEABLE) {
if (m == NULL || (m->flags & PG_WRITEABLE) == 0)
rv = vm_fault(map, va, VM_PROT_WRITE, VM_FAULT_DIRTY);
} else if (m == NULL)
if (m)
continue;
if (flags & VM_HOLD_WRITEABLE)
rv = vm_fault(map, va, VM_PROT_WRITE, VM_FAULT_DIRTY);
else
rv = vm_fault(map, va, VM_PROT_READ, VM_FAULT_NORMAL);
if (rv) {
printf("vm_fault bad return rv=%d va=0x%zx\n", rv, va);