Changeset View
Standalone View
sys/vm/vm_fault.c
Show First 20 Lines • Show All 833 Lines • ▼ Show 20 Lines | while (TRUE) { | ||||
/* | /* | ||||
* Page is not resident. If the pager might contain the page | * Page is not resident. If the pager might contain the page | ||||
* or this is the beginning of the search, allocate a new | * or this is the beginning of the search, allocate a new | ||||
* page. (Default objects are zero-fill, so there is no real | * page. (Default objects are zero-fill, so there is no real | ||||
* pager for them.) | * pager for them.) | ||||
*/ | */ | ||||
if (fs.object->type != OBJT_DEFAULT || | if (fs.object->type != OBJT_DEFAULT || | ||||
fs.object == fs.first_object) { | fs.object == fs.first_object) { | ||||
if (fs.pindex >= fs.object->size) { | /* | ||||
* The vnode case is handled later, after the | |||||
* vnode is locked and just before pagein. | |||||
*/ | |||||
if (fs.object->type != OBJT_VNODE && | |||||
fs.pindex >= fs.object->size) { | |||||
markj: I would note here that the vnode case is handled later, after the vnode is locked and just… | |||||
unlock_and_deallocate(&fs); | unlock_and_deallocate(&fs); | ||||
return (KERN_OUT_OF_BOUNDS); | return (KERN_OUT_OF_BOUNDS); | ||||
} | } | ||||
if (fs.object == fs.first_object && | if (fs.object == fs.first_object && | ||||
(fs.first_object->flags & OBJ_POPULATE) != 0 && | (fs.first_object->flags & OBJ_POPULATE) != 0 && | ||||
fs.first_object->shadow_count == 0) { | fs.first_object->shadow_count == 0) { | ||||
rv = vm_fault_populate(&fs, prot, fault_type, | rv = vm_fault_populate(&fs, prot, fault_type, | ||||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | if (fs.object->type != OBJT_DEFAULT) { | ||||
LK_CANRECURSE, curthread); | LK_CANRECURSE, curthread); | ||||
vdrop(vp); | vdrop(vp); | ||||
fs.vp = vp; | fs.vp = vp; | ||||
KASSERT(error == 0, | KASSERT(error == 0, | ||||
("vm_fault: vget failed")); | ("vm_fault: vget failed")); | ||||
goto RetryFault; | goto RetryFault; | ||||
} | } | ||||
fs.vp = vp; | fs.vp = vp; | ||||
} | |||||
if (fs.object->type == OBJT_VNODE && | |||||
fs.pindex >= fs.object->size) { | |||||
unlock_and_deallocate(&fs); | |||||
return (KERN_OUT_OF_BOUNDS); | |||||
} | } | ||||
alcUnsubmitted Not Done Inline ActionsCan you please elaborate on how this delayed size check helps? The object's size field is only changed with object lock held. Yes? And, I believe that the object lock is held continuously from the earlier size check until this new one. In other words, how does the size field change between the earlier check and this new one? alc: Can you please elaborate on how this delayed size check helps? The object's size field is only… | |||||
kibAuthorUnsubmitted Done Inline ActionsYes, of course there is no issues with the consistency for the vm_fault() itself. The problem is in inconsistency of user-visible file vs. mapping state. Since for instance iod's finish io without taking the NFS vnode lock, it might be possible for userspace to see larger (extended) file while the vnode_pager_setsize() call from iod is blocked on the object lock, thus vm_fault() effectively sees shorter file. The result is a spurious SIGSEGV that was reported. kib: Yes, of course there is no issues with the consistency for the vm_fault() itself. The problem… | |||||
KASSERT(fs.vp == NULL || !fs.map->system_map, | KASSERT(fs.vp == NULL || !fs.map->system_map, | ||||
("vm_fault: vnode-backed object mapped by system map")); | ("vm_fault: vnode-backed object mapped by system map")); | ||||
/* | /* | ||||
* Page in the requested page and hint the pager, | * Page in the requested page and hint the pager, | ||||
* that it may bring up surrounding pages. | * that it may bring up surrounding pages. | ||||
*/ | */ | ||||
if (nera == -1 || behavior == MAP_ENTRY_BEHAV_RANDOM || | if (nera == -1 || behavior == MAP_ENTRY_BEHAV_RANDOM || | ||||
▲ Show 20 Lines • Show All 863 Lines • Show Last 20 Lines |
I would note here that the vnode case is handled later, after the vnode is locked and just before pagein.