Index | Thread | Search

From:
Martin Pieuchot <mpi@grenadille.net>
Subject:
Re: diagprintf(9) aka Syzkaller debugging
To:
tech@openbsd.org
Date:
Mon, 15 Dec 2025 14:37:23 +0100

Download raw body.

Thread
On 11/12/25(Thu) 14:58, Martin Pieuchot wrote:
> Thanks to the recently added KASSERT(), syzkaller exposed an
> inconsistency bug where a physical page ends up on a managed list and
> with a non-0 `wired_count':
>   https://syzkaller.appspot.com/bug?extid=f3c3aa16434bc13e3138
>   https://syzkaller.appspot.com/bug?extid=c1461974b88117f479c9
> 
> It's not clear to me how this can happen.  So I'd like to get more
> informations.
> Syzkaller reports anything after 'panic' and I'd like to print per-page
> informations (call uvm_page_printit()).  So I added a new printf(9)-like
> function that prints to a static buffer:
> 
> panic: page on LRU PAGE 0xfffffd8002dd5a58:
>   flags=200004<TABLED,AOBJ>, vers=1, wire_count=0, pa=0x74c7000
>   uobject=0xfffffd8007575f30, uanon=0x0, offset=0x0
>   [page ownership tracking disabled]    vm_page_md 0xfffffd8002dd5ac0
>   checking object list
>   page found on object list

Here's a more specific approach following feedbacks from deraadt@.  I'm
also looking into something that can be committed because there's no way
to test this as syzkaller has no reproducer (yet) for this bug.

Comments?  Oks?

Index: uvm/uvm_page.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_page.c,v
diff -u -p -r1.184 uvm_page.c
--- uvm/uvm_page.c	10 Dec 2025 08:38:18 -0000	1.184
+++ uvm/uvm_page.c	15 Dec 2025 13:08:25 -0000
@@ -75,6 +75,7 @@
 #include <sys/smr.h>
 
 #include <uvm/uvm.h>
+#include <uvm/uvm_ddb.h>
 
 /*
  * for object trees
@@ -1219,6 +1220,42 @@ uvm_pagelookup(struct uvm_object *obj, v
 	return (pg);
 }
 
+#ifdef DIAGNOSTIC
+char	diagbuf[1024];		/* static buffer for uvm_page_printit(). */
+int	diagpr(const char *, ...) __attribute__((__format__(__kprintf__,1,2)));
+
+int
+diagpr(const char *fmt, ...)
+{
+	static int diaglen;
+	int len, retval;
+	va_list ap;
+
+	len = sizeof(diagbuf) - diaglen;
+	if (len <= 1)
+		return -1;
+	va_start(ap, fmt);
+	retval = vsnprintf(diagbuf + diaglen, len, fmt, ap);
+	va_end(ap);
+
+	diaglen += retval;
+	return retval;
+}
+#endif /* DIAGNOSTIC */
+
+void
+assert_not_managed(struct vm_page *pg)
+{
+#ifdef DIAGNOSTIC
+	if (pg->pg_flags & (PQ_INACTIVE|PQ_ACTIVE)) {
+#ifdef DDB
+		uvm_page_printit(pg, TRUE, diagpr);
+#endif
+		panic("page on LRU %s", diagbuf);
+	}
+#endif
+}
+
 /*
  * uvm_pagewire: wire the page, thus removing it from the daemon's grasp
  */
@@ -1233,6 +1270,7 @@ uvm_pagewire(struct vm_page *pg)
 		uvm_unlock_pageq();
 		atomic_inc_int(&uvmexp.wired);
 	}
+	assert_not_managed(pg);
 	pg->wire_count++;
 }
 
@@ -1264,7 +1302,7 @@ uvm_pagedeactivate(struct vm_page *pg)
 	KASSERT(uvm_page_owner_locked_p(pg, FALSE));
 
 	if (pg->wire_count > 0) {
-		KASSERT((pg->pg_flags & (PQ_INACTIVE|PQ_ACTIVE)) == 0);
+		assert_not_managed(pg);
 		return;
 	}
 
Index: uvm/uvm_map.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_map.c,v
diff -u -p -r1.349 uvm_map.c
--- uvm/uvm_map.c	10 Dec 2025 08:38:18 -0000	1.349
+++ uvm/uvm_map.c	11 Dec 2025 11:54:02 -0000
@@ -2947,7 +2947,7 @@ uvm_object_printit(struct uvm_object *uo
  */
 static const char page_flagbits[] =
 	"\20\1BUSY\2WANTED\3TABLED\4CLEAN\5CLEANCHK\6RELEASED\7FAKE\10RDONLY"
-	"\11ZERO\12DEV\15PAGER1\21FREE\22INACTIVE\23ACTIVE\25ANON\26AOBJ"
+	"\11ZERO\12DEV\21FREE\22INACTIVE\23ACTIVE\25ANON\26AOBJ"
 	"\27ENCRYPT\31PMAP0\32PMAP1\33PMAP2\34PMAP3\35PMAP4\36PMAP5";
 
 void