Download raw body.
diagprintf(9) aka Syzkaller debugging
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
Comments, ok?
Index: kern/subr_prf.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_prf.c,v
diff -u -p -r1.106 subr_prf.c
--- kern/subr_prf.c 14 Aug 2022 01:58:28 -0000 1.106
+++ kern/subr_prf.c 11 Dec 2025 13:48:08 -0000
@@ -115,6 +115,10 @@ int db_console = 0;
#endif
#endif
+#ifdef DIAGNOSTIC
+char diagbuf[1024]; /* static buffer for diagnostic. */
+#endif
+
/*
* panic on spl assertion failure?
*/
@@ -510,6 +514,32 @@ db_vprintf(const char *fmt, va_list ap)
}
#endif /* DDB */
+#ifdef DIAGNOSTIC
+/*
+ * diagprintf: print a message to the static buffer `diagbuf'
+ */
+int
+diagprintf(const char *fmt, ...)
+{
+ static char *diagp;
+ int retval;
+ va_list ap;
+ char *p;
+
+ if (diagp == NULL)
+ diagp = diagbuf;
+ p = diagbuf + sizeof(diagbuf) - 1;
+ /* check for buffer exhaustion */
+ if (p <= diagp)
+ return (-1);
+ va_start(ap, fmt);
+ retval = kprintf(fmt, TOBUFONLY | TOCOUNT, &p, diagp, ap);
+ va_end(ap);
+ *p = '\0';
+ diagp = p;
+ return (retval);
+}
+#endif /* DIAGNOSTIC */
/*
* normal kernel printf functions: printf, vprintf, snprintf
Index: sys/systm.h
===================================================================
RCS file: /cvs/src/sys/sys/systm.h,v
diff -u -p -r1.177 systm.h
--- sys/systm.h 28 Jul 2025 05:08:35 -0000 1.177
+++ sys/systm.h 11 Dec 2025 13:27:59 -0000
@@ -175,6 +175,11 @@ int snprintf(char *buf, size_t, const ch
struct tty;
void ttyprintf(struct tty *, const char *, ...)
__attribute__((__format__(__kprintf__,2,3)));
+#ifdef DIAGNOSTIC
+extern char diagbuf[1024];
+int diagprintf(const char *, ...)
+ __attribute__((__format__(__kprintf__,1,2)));
+#endif
void splassert_fail(int, int, const char *);
extern int splassert_ctl;
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
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 11 Dec 2025 13:55:53 -0000
@@ -75,6 +75,7 @@
#include <sys/smr.h>
#include <uvm/uvm.h>
+#include <uvm/uvm_ddb.h>
/*
* for object trees
@@ -1219,6 +1220,19 @@ uvm_pagelookup(struct uvm_object *obj, v
return (pg);
}
+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, diagprintf);
+#endif
+ panic("page on LRU %s", diagbuf);
+ }
+#endif
+}
+
/*
* uvm_pagewire: wire the page, thus removing it from the daemon's grasp
*/
@@ -1233,6 +1247,7 @@ uvm_pagewire(struct vm_page *pg)
uvm_unlock_pageq();
atomic_inc_int(&uvmexp.wired);
}
+ assert_not_managed(pg);
pg->wire_count++;
}
@@ -1264,7 +1279,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;
}
diagprintf(9) aka Syzkaller debugging