svn commit: r354400 - head/sys/vm

Mark Johnston markj at FreeBSD.org
Wed Nov 6 16:59:17 UTC 2019


Author: markj
Date: Wed Nov  6 16:59:16 2019
New Revision: 354400
URL: https://svnweb.freebsd.org/changeset/base/354400

Log:
  Fix a race in release_page().
  
  Since r354156 we may call release_page() without the page's object lock
  held, specifically following the page copy during a CoW fault.
  release_page() must therefore unbusy the page only after scheduling the
  requeue, to avoid racing with a free of the page.  Previously, the
  object lock prevented this race from occurring.
  
  Add some assertions that were helpful in tracking this down.
  
  Reported by:	pho, syzkaller
  Tested by:	pho
  Reviewed by:	alc, jeff, kib
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D22234

Modified:
  head/sys/vm/vm_fault.c
  head/sys/vm/vm_page.c

Modified: head/sys/vm/vm_fault.c
==============================================================================
--- head/sys/vm/vm_fault.c	Wed Nov  6 16:21:35 2019	(r354399)
+++ head/sys/vm/vm_fault.c	Wed Nov  6 16:59:16 2019	(r354400)
@@ -155,10 +155,14 @@ release_page(struct faultstate *fs)
 {
 
 	if (fs->m != NULL) {
-		vm_page_xunbusy(fs->m);
+		/*
+		 * fs->m's object lock might not be held, so the page must be
+		 * kept busy until we are done with it.
+		 */
 		vm_page_lock(fs->m);
 		vm_page_deactivate(fs->m);
 		vm_page_unlock(fs->m);
+		vm_page_xunbusy(fs->m);
 		fs->m = NULL;
 	}
 }

Modified: head/sys/vm/vm_page.c
==============================================================================
--- head/sys/vm/vm_page.c	Wed Nov  6 16:21:35 2019	(r354399)
+++ head/sys/vm/vm_page.c	Wed Nov  6 16:59:16 2019	(r354400)
@@ -3421,7 +3421,7 @@ vm_page_dequeue(vm_page_t m)
 	struct vm_pagequeue *pq, *pq1;
 	uint8_t aflags;
 
-	KASSERT(mtx_owned(vm_page_lockptr(m)) || m->object == NULL,
+	KASSERT(mtx_owned(vm_page_lockptr(m)) || m->ref_count == 0,
 	    ("page %p is allocated and unlocked", m));
 
 	for (pq = vm_page_pagequeue(m);; pq = pq1) {
@@ -3475,6 +3475,8 @@ vm_page_enqueue(vm_page_t m, uint8_t queue)
 	vm_page_assert_locked(m);
 	KASSERT(m->queue == PQ_NONE && (m->aflags & PGA_QUEUE_STATE_MASK) == 0,
 	    ("%s: page %p is already enqueued", __func__, m));
+	KASSERT(m->ref_count > 0,
+	    ("%s: page %p does not carry any references", __func__, m));
 
 	m->queue = queue;
 	if ((m->aflags & PGA_REQUEUE) == 0)
@@ -3496,6 +3498,8 @@ vm_page_requeue(vm_page_t m)
 	vm_page_assert_locked(m);
 	KASSERT(vm_page_queue(m) != PQ_NONE,
 	    ("%s: page %p is not logically enqueued", __func__, m));
+	KASSERT(m->ref_count > 0,
+	    ("%s: page %p does not carry any references", __func__, m));
 
 	if ((m->aflags & PGA_REQUEUE) == 0)
 		vm_page_aflag_set(m, PGA_REQUEUE);
@@ -3889,6 +3893,8 @@ vm_page_mvqueue(vm_page_t m, const uint8_t nqueue)
 	vm_page_assert_locked(m);
 	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
 	    ("vm_page_mvqueue: page %p is unmanaged", m));
+	KASSERT(m->ref_count > 0,
+	    ("%s: page %p does not carry any references", __func__, m));
 
 	if (vm_page_queue(m) != nqueue) {
 		vm_page_dequeue(m);


More information about the svn-src-all mailing list