aboutsummaryrefslogtreecommitdiff
path: root/sys/vm/vm_fault.c
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2025-04-13 16:09:31 +0000
committerMark Johnston <markj@FreeBSD.org>2025-05-13 12:50:51 +0000
commitbd20de7d16c768f00419602a0eb2ed93b942bf0e (patch)
tree47c5ba948b0d03b00cead0053dd595410cfdc0d0 /sys/vm/vm_fault.c
parent6180a8eaba89e7dcb609aa89419b9629108a6b87 (diff)
Diffstat (limited to 'sys/vm/vm_fault.c')
-rw-r--r--sys/vm/vm_fault.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c
index dd044d27f00f..9d7c5d452a0b 100644
--- a/sys/vm/vm_fault.c
+++ b/sys/vm/vm_fault.c
@@ -1050,14 +1050,14 @@ vm_fault_cow(struct faultstate *fs)
* Oh, well, lets copy it.
*/
pmap_copy_page(fs->m, fs->first_m);
- vm_page_valid(fs->first_m);
if (fs->wired && (fs->fault_flags & VM_FAULT_WIRE) == 0) {
vm_page_wire(fs->first_m);
vm_page_unwire(fs->m, PQ_INACTIVE);
}
/*
- * Save the cow page to be released after
- * pmap_enter is complete.
+ * Save the COW page to be released after pmap_enter is
+ * complete. The new copy will be marked valid when we're ready
+ * to map it.
*/
fs->m_cow = fs->m;
fs->m = NULL;
@@ -1741,6 +1741,19 @@ found:
fs.entry->next_read = vaddr + ptoa(ahead) + PAGE_SIZE;
/*
+ * If the page to be mapped was copied from a backing object, we defer
+ * marking it valid until here, where the fault handler is guaranteed to
+ * succeed. Otherwise we can end up with a shadowed, mapped page in the
+ * backing object, which violates an invariant of vm_object_collapse()
+ * that shadowed pages are not mapped.
+ */
+ if (fs.m_cow != NULL) {
+ KASSERT(vm_page_none_valid(fs.m),
+ ("vm_fault: page %p is already valid", fs.m_cow));
+ vm_page_valid(fs.m);
+ }
+
+ /*
* Page must be completely valid or it is not fit to
* map into user space. vm_pager_get_pages() ensures this.
*/