--- a/sys/vfs/hammer2/hammer2_admin.c 2026-08-28 15:24:47.450135496 +0000 +++ b/sys/vfs/hammer2/hammer2_admin.c 2026-08-28 15:25:09.529848981 +0000 @@ -421,7 +421,40 @@ /* * A mounted PFS needs Xops threads to support frontend operations. + * + * DF2621 VERIFICATION INSTRUMENTATION (temporary, in-guest build only). */ +static hammer2_pfs_t *df2621_freed_ring[16]; +static int df2621_freed_ridx; +static int df2621_uaf_reports; + +void hammer2_df2621_mark_free(hammer2_pfs_t *pmp); +void +hammer2_df2621_mark_free(hammer2_pfs_t *pmp) +{ + df2621_freed_ring[df2621_freed_ridx++ & 15] = pmp; + kprintf("DF2621: pfsfree kfree pmp=%p\n", pmp); +} + +static void +df2621_check_freed(hammer2_thread_t *thr, hammer2_pfs_t *pmp) +{ + int i; + + if (pmp == NULL || df2621_uaf_reports >= 200) + return; + for (i = 0; i < 16; ++i) { + if (df2621_freed_ring[i] == pmp) { + ++df2621_uaf_reports; + kprintf("DF2621: UAF-WRITE thr=%p clindex=%d " + "spin_ex(&pmp->xop_spin) on FREED pmp=%p " + "(report %d)\n", + thr, thr->clindex, pmp, df2621_uaf_reports); + return; + } + } +} + void hammer2_xop_helper_create(hammer2_pfs_t *pmp) { @@ -431,9 +464,14 @@ lockmgr(&pmp->lock, LK_EXCLUSIVE); pmp->has_xop_threads = 1; + kprintf("DF2621: helper_create pmp=%p old_groups=%p nchains=%d\n", + pmp, pmp->xop_groups, + pmp->iroot ? pmp->iroot->cluster.nchains : -1); pmp->xop_groups = kmalloc(hammer2_xop_nthreads * sizeof(hammer2_xop_group_t), M_HAMMER2, M_WAITOK | M_ZERO); + kprintf("DF2621: helper_create pmp=%p new_groups=%p (OLD LEAKED)\n", + pmp, pmp->xop_groups); for (i = 0; i < pmp->iroot->cluster.nchains; ++i) { for (j = 0; j < hammer2_xop_nthreads; ++j) { if (pmp->xop_groups[j].thrs[i].td) @@ -453,6 +491,8 @@ int i; int j; + kprintf("DF2621: helper_cleanup enter pmp=%p groups=%p nmasters=%d\n", + pmp, pmp->xop_groups, pmp->pfs_nmasters); if (pmp->xop_groups == NULL) { KKASSERT(pmp->has_xop_threads == 0); return; @@ -463,8 +503,11 @@ if (pmp->xop_groups[j].thrs[i].td) hammer2_thr_delete(&pmp->xop_groups[j].thrs[i]); } + kprintf("DF2621: helper_cleanup col %d deleted\n", i); } pmp->has_xop_threads = 0; + kprintf("DF2621: helper_cleanup freeing groups=%p\n", + pmp->xop_groups); kfree(pmp->xop_groups, M_HAMMER2); pmp->xop_groups = NULL; } @@ -1076,6 +1119,7 @@ uint32_t hash[XOP_HASH_SIZE] = { 0 }; hammer2_xop_head_t *xop; + df2621_check_freed(thr, pmp); hammer2_spin_ex(&pmp->xop_spin); TAILQ_FOREACH(xop, &thr->xopq, collect[clindex].entry) { /* --- a/sys/vfs/hammer2/hammer2_vfsops.c 2026-08-28 15:24:47.454135445 +0000 +++ b/sys/vfs/hammer2/hammer2_vfsops.c 2026-08-28 15:25:09.545848774 +0000 @@ -718,7 +718,9 @@ if (chains_still_present) { kprintf("hammer2: cannot free pmp %p, still in use\n", pmp); } else { + extern void hammer2_df2621_mark_free(hammer2_pfs_t *); kmalloc_destroy_obj(&pmp->minode); + hammer2_df2621_mark_free(pmp); kfree(pmp, M_HAMMER2); } } @@ -743,6 +745,7 @@ wlist = &hammer2_pfslist; else wlist = &hammer2_spmplist; + kprintf("DF2621: pfsfree_scan hmp=%p which=%d enter\n", hmp, which); again: TAILQ_FOREACH(pmp, wlist, mntentry) { if ((iroot = pmp->iroot) == NULL) @@ -769,6 +772,8 @@ * Make sure all synchronization threads are locked * down. */ + kprintf("DF2621: pfsfree_scan freeze begin pmp=%p groups=%p\n", + pmp, pmp->xop_groups); for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) { if (pmp->pfs_hmps[i] == NULL) continue; @@ -807,6 +812,8 @@ /* * Remove the chain from matching elements of the PFS. */ + kprintf("DF2621: pfsfree_scan delete cols begin pmp=%p hmp=%p\n", + pmp, hmp); for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) { if (pmp->pfs_hmps[i] != hmp) continue; @@ -863,6 +870,7 @@ */ KKASSERT(TAILQ_EMPTY(&pmp->syncq)); KKASSERT(TAILQ_EMPTY(&pmp->depq)); + kprintf("DF2621: pfsfree_scan pmp empty -> pfsfree\n"); hammer2_pfsfree(pmp); goto again; } @@ -871,6 +879,7 @@ * If elements still remain we need to set the REMASTER * flag and unfreeze it. */ + kprintf("DF2621: pfsfree_scan unfreeze+remaster pmp=%p\n", pmp); for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) { if (pmp->pfs_hmps[i] == NULL) continue;