From: Martin Pieuchot Subject: Re: diagprintf(9) aka Syzkaller debugging To: tech@openbsd.org Date: Mon, 15 Dec 2025 14:37:23 +0100 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, 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 #include +#include /* * 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