If the vm_fault() handler raced with the vm_object_collapse()
sleepable scan, iteration over the shadow chain looking for a page could find an OBJ_DEAD object. Such state of the mapping is only transient, the dead object will be terminated and removed from the chain shortly. We must not return KERN_PROTECTION_FAILURE unless the object type is changed to OBJT_DEAD in the chain, indicating that paging on this address is really impossible. Returning KERN_PROTECTION_FAILURE prematurely causes spurious SIGSEGV delivered to processes, or kernel accesses to UVA spuriously failing with EFAULT. If the object with OBJ_DEAD flag is found, only return KERN_PROTECTION_FAILURE when object type is already OBJT_DEAD. Otherwise, sleep a tick and retry the fault handling. Ideally, we would wait until the OBJ_DEAD flag is resolved, e.g. by waiting until the paging on this object is finished. But to do so, we need to reference the dead object, while vm_object_collapse() insists on owning the final reference on the collapsed object. This could be fixed by e.g. changing the assert to shared reference release between vm_fault() and vm_object_collapse(), but it seems to be too much complications for rare boundary condition. PR: 204426 Tested by: pho Reviewed by: alc Sponsored by: The FreeBSD Foundation X-Differential revision: https://reviews.freebsd.org/D6085 MFC after: 2 weeks Approved by: re (gjb)
This commit is contained in:
parent
9e590ff04b
commit
725441f69b
@ -292,7 +292,7 @@ vm_fault_hold(vm_map_t map, vm_offset_t vaddr, vm_prot_t fault_type,
|
||||
struct faultstate fs;
|
||||
struct vnode *vp;
|
||||
vm_page_t m;
|
||||
int ahead, behind, cluster_offset, error, locked;
|
||||
int ahead, behind, cluster_offset, dead, error, locked;
|
||||
|
||||
hardfault = 0;
|
||||
growstack = TRUE;
|
||||
@ -421,11 +421,18 @@ RetryFault:;
|
||||
fs.pindex = fs.first_pindex;
|
||||
while (TRUE) {
|
||||
/*
|
||||
* If the object is dead, we stop here
|
||||
* If the object is marked for imminent termination,
|
||||
* we retry here, since the collapse pass has raced
|
||||
* with us. Otherwise, if we see terminally dead
|
||||
* object, return fail.
|
||||
*/
|
||||
if (fs.object->flags & OBJ_DEAD) {
|
||||
if ((fs.object->flags & OBJ_DEAD) != 0) {
|
||||
dead = fs.object->type == OBJT_DEAD;
|
||||
unlock_and_deallocate(&fs);
|
||||
return (KERN_PROTECTION_FAILURE);
|
||||
if (dead)
|
||||
return (KERN_PROTECTION_FAILURE);
|
||||
pause("vmf_de", 1);
|
||||
goto RetryFault;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user