/*
 * dfv_safe.c — DF-2889/2890/2907(safe)/2970/2971/2985/2988 verification rig.
 * All ops are read-only or module-local; none of them panic.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mchain.h>
#include <sys/conf.h>
#include <sys/sysent.h>
#include <sys/sysmsg.h>
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/thread2.h>
#include <sys/kthread.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/tree.h>
#include <sys/time.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <sys/vmmeter.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/ucred.h>
#include <sys/nlookup.h>
#include <sys/namecache.h>
#include <sys/vfsops.h>


/* ------------------------------------------------------------------ */
/* DF-2889: vm_contig_pg_clean color-queue predicate is wrong          */
/* ------------------------------------------------------------------ */
static int
dfv_q2889(SYSCTL_HANDLER_ARGS)
{
	struct vm_page marker;
	struct vpgqueues *pq;
	vm_page_t m;
	int error, q, queue;
	long total = 0, pass_old = 0, pass_fixed = 0;
	long qn = 0, q0total = 0, q0pass = 0;
	int scanned;

	error = sysctl_handle_int(oidp, &q, 0, req);
	if (error || req->newptr == NULL)
		return (error);

	/* q=1..1023: every page is skipped by the buggy predicate */
	for (q = 1; q < PQ_L2_SIZE; q++) {
		queue = PQ_INACTIVE + q;
		pq = &vm_page_queues[queue];
		scanned = 0;

		bzero(&marker, sizeof(marker));
		marker.flags = PG_FICTITIOUS | PG_MARKER;
		marker.busy_count = PBUSY_LOCKED;
		marker.queue = queue;
		marker.wire_count = 1;

		vm_page_queues_spin_lock(queue);
		TAILQ_INSERT_HEAD(&pq->pl, &marker, pageq);
		vm_page_queues_spin_unlock(queue);

		while ((m = TAILQ_NEXT(&marker, pageq)) != NULL && scanned < 20000) {
			vm_page_and_queue_spin_lock(m);
			if (m != TAILQ_NEXT(&marker, pageq)) {
				vm_page_and_queue_spin_unlock(m);
				continue;
			}
			++scanned;
			/* kernel check at vm_contig.c:194 */
			if (!(m->queue - m->pc != queue))
				pass_old++;
			/* intended check */
			if (!(m->queue != queue))
				pass_fixed++;
			total++;

			TAILQ_REMOVE(&pq->pl, &marker, pageq);
			TAILQ_INSERT_AFTER(&pq->pl, m, &marker, pageq);
			vm_page_and_queue_spin_unlock(m);
		}
		if (scanned)
			qn++;
		vm_page_queues_spin_lock(queue);
		TAILQ_REMOVE(&pq->pl, &marker, pageq);
		vm_page_queues_spin_unlock(queue);
	}
	/* q=0 for contrast */
	{
		queue = PQ_INACTIVE + 0;
		pq = &vm_page_queues[queue];
		bzero(&marker, sizeof(marker));
		marker.flags = PG_FICTITIOUS | PG_MARKER;
		marker.busy_count = PBUSY_LOCKED;
		marker.queue = queue;
		marker.wire_count = 1;
		vm_page_queues_spin_lock(queue);
		TAILQ_INSERT_HEAD(&pq->pl, &marker, pageq);
		vm_page_queues_spin_unlock(queue);
		while ((m = TAILQ_NEXT(&marker, pageq)) != NULL && q0total < 20000) {
			vm_page_and_queue_spin_lock(m);
			if (m != TAILQ_NEXT(&marker, pageq)) {
				vm_page_and_queue_spin_unlock(m);
				continue;
			}
			if (!(m->queue - m->pc != queue))
				q0pass++;
			q0total++;
			TAILQ_REMOVE(&pq->pl, &marker, pageq);
			TAILQ_INSERT_AFTER(&pq->pl, m, &marker, pageq);
			vm_page_and_queue_spin_unlock(m);
		}
		vm_page_queues_spin_lock(queue);
		TAILQ_REMOVE(&pq->pl, &marker, pageq);
		vm_page_queues_spin_unlock(queue);
	}

	kprintf("DF2889: INACTIVE queues q=1..1023: %ld pages in %ld nonempty "
		"queues; buggy :194 predicate passes %ld (ALL skipped => zero "
		"reclaim from colored queues); fixed predicate (m->queue != queue) "
		"passes %ld. q=0: %ld pages, buggy passes %ld.\n",
		total, qn, pass_old, pass_fixed, q0total, q0pass);
	return (0);
}

/* ------------------------------------------------------------------ */
/* DF-2890: verify/alloc loops lack vm_page_count bound                */
/* ------------------------------------------------------------------ */
static int
dfv_cg2890(SYSCTL_HANDLER_ARGS)
{
	vm_page_t pga = vm_page_array;
	long count = (long)vmstats.v_page_count;
	vm_paddr_t phys;
	unsigned long size;
	long i, start, size_pages, oob = 0, inb = 0;
	int error, q = 0;

	error = sysctl_handle_int(oidp, &q, 0, req);
	if (error || req->newptr == NULL)
		return (error);

	start = count - 4;
	size_pages = count + 64;			/* exceeds the array */
	size = size_pages * PAGE_SIZE;

	/* replicate vm_contig.c:377 loop, instrumented */
	for (i = start + 1; i < (long)(start + size / PAGE_SIZE); i++) {
		if (i >= count) {
			if (oob < 4) {
				kprintf("DF2890: OOB READ idx=%ld (v_page_count"
					"=%ld): &pga[i]=%p word0=0x%lx\n",
					i, count, (void *)&pga[i],
					*(volatile unsigned long *)&pga[i]);
			}
			oob++;
			if (oob >= 4)
				break;
		} else {
			inb++;
		}
	}

	/* replicate the :331 pre-check with high = BUS_SPACE_MAXADDR */
	phys = VM_PAGE_TO_PHYS(&pga[start]);
	kprintf("DF2890: v_page_count=%ld array=%p..%p; candidate start=%ld "
		"phys=0x%llx size=0x%lx(%ld pages)\n", count, (void *)pga,
		(void *)&pga[count-1], start, (unsigned long long)phys,
		size, size_pages);
	kprintf("DF2890: :331 pre-check (phys+size > high) with "
		"high=BUS_SPACE_MAXADDR: phys+size=0x%llx -> %s "
		"(check passes, loop runs to %ld)\n",
		(unsigned long long)(phys + size),
		(phys + size) > 0xFFFFFFFFFFFFFFFFUL ? "BLOCKS" : "PASSES",
		(long)(start + size / PAGE_SIZE));
	kprintf("DF2890: wrap case: size=-(phys)|... phys+size wraps below "
		"high -> also PASSES. in-bounds iters=%ld OOB reads=%ld\n",
		inb, oob);
	return (0);
}

/* ------------------------------------------------------------------ */
/* DF-2907 (safe part): md_get_mbuf ignores md_get_mem; md_done        */
/* leaves md_cur/md_pos dangling                                       */
/* ------------------------------------------------------------------ */
static int
dfv_mc2907(SYSCTL_HANDLER_ARGS)
{
	struct mbchain mb;
	struct mdchain md;
	struct mbuf *m = NULL, *saved_cur;
	u_char data[64];
	int r, error, q = 0;

	error = sysctl_handle_int(oidp, &q, 0, req);
	if (error || req->newptr == NULL)
		return (error);

	memset(data, 0x42, sizeof(data));
	r = mb_init(&mb);
	kprintf("DF2907: mb_init rc=%d\n", r);
	mb_put_mem(&mb, data, sizeof(data), MB_MSYSTEM);
	md_initm(&md, mb_detach(&mb));
	kprintf("DF2907: chain len=%d; md_cur=%p md_pos=%p\n",
		md.md_top->m_pkthdr.len, md.md_cur, md.md_pos);

	r = md_get_mbuf(&md, 32, &m);		/* fits: proper path */
	kprintf("DF2907: md_get_mbuf(32) rc=%d m=%p m_len=%d\n",
		r, m, m ? m->m_len : -1);
	if (m) {
		m_freem(m);
		m = NULL;
	}

	saved_cur = md.md_cur;
	kprintf("DF2907: before md_done: md_top=%p md_cur=%p\n",
		(void *)md.md_top, (void *)md.md_cur);
	md_done(&md);
	kprintf("DF2907: after  md_done: md_top=%p md_cur=%p md_pos=%p "
		"<- md_cur/md_pos DANGLING\n",
		(void *)md.md_top, (void *)md.md_cur, (void *)md.md_pos);
	kprintf("DF2907: stale UAF read via md_cur->m_len after md_done: %d\n",
		saved_cur->m_len);
	return (0);
}

/* ------------------------------------------------------------------ */
/* DF-2970 + DF-2971: linesw[] registry                                */
/* ------------------------------------------------------------------ */
static int fka_open(cdev_t, struct tty *);
static int fka_close(struct tty *, int);
static int fka_read(struct tty *, struct uio *, int);
static int fka_write(struct tty *, struct uio *, int);
static int fka_ioctl(struct tty *, u_long, caddr_t, int, struct ucred *);
static int fka_rint(int, struct tty *);
static int fka_start(struct tty *);
static int fka_modem(struct tty *, int);
static int fkb_open(cdev_t, struct tty *);
static int fkb_close(struct tty *, int);
static int fkb_read(struct tty *, struct uio *, int);
static int fkb_write(struct tty *, struct uio *, int);
static int fkb_ioctl(struct tty *, u_long, caddr_t, int, struct ucred *);
static int fkb_rint(int, struct tty *);
static int fkb_start(struct tty *);
static int fkb_modem(struct tty *, int);

static int opens_a, closes_a, opens_b, closes_b;
static struct linesw saved6;
static struct linesw fakeA = {
	.l_open = fka_open, .l_close = fka_close, .l_read = fka_read,
	.l_write = fka_write, .l_ioctl = fka_ioctl, .l_rint = fka_rint,
	.l_start = fka_start, .l_modem = fka_modem, .l_hotchar = 0
};
static struct linesw fakeB = {
	.l_open = fkb_open, .l_close = fkb_close, .l_read = fkb_read,
	.l_write = fkb_write, .l_ioctl = fkb_ioctl, .l_rint = fkb_rint,
	.l_start = fkb_start, .l_modem = fkb_modem, .l_hotchar = 0
};

static int
fka_open(cdev_t d, struct tty *t) { opens_a++; return (0); }
static int
fka_close(struct tty *t, int f) { closes_a++; return (0); }
static int
fka_read(struct tty *t, struct uio *u, int f) { return (0); }
static int
fka_write(struct tty *t, struct uio *u, int f) { return (0); }
static int
fka_ioctl(struct tty *t, u_long c, caddr_t d, int f, struct ucred *cr)
	{ return (ENOTTY); }
static int
fka_rint(int c, struct tty *t) { return (0); }
static int
fka_start(struct tty *t) { return (0); }
static int
fka_modem(struct tty *t, int f) { return (0); }
static int
fkb_open(cdev_t d, struct tty *t) { opens_b++; return (0); }
static int
fkb_close(struct tty *t, int f) { closes_b++; return (0); }
static int
fkb_read(struct tty *t, struct uio *u, int f) { return (0); }
static int
fkb_write(struct tty *t, struct uio *u, int f) { return (0); }
static int
fkb_ioctl(struct tty *t, u_long c, caddr_t d, int f, struct ucred *cr)
	{ return (ENOTTY); }
static int
fkb_rint(int c, struct tty *t) { return (0); }
static int
fkb_start(struct tty *t) { return (0); }
static int
fkb_modem(struct tty *t, int f) { return (0); }

static int
dfv_l2970(SYSCTL_HANDLER_ARGS)
{
	int error, phase = 0;

	error = sysctl_handle_int(oidp, &phase, 0, req);
	if (error || req->newptr == NULL)
		return (error);

	switch (phase) {
	case 1:
		saved6 = linesw[6];
		kprintf("DF2970: linesw[6] (NETGRAPHDISC) live l_open=%p\n",
			linesw[6].l_open);
		{
			int slot = ldisc_register(6, &fakeA);
			kprintf("DF2970: ldisc_register(6, fake) -> slot=%d "
				"SILENTLY ACCEPTED (no occupied-slot check); "
				"new l_open=%p\n", slot, linesw[6].l_open);
		}
		break;
	case 2:
		ldisc_deregister(6);
		kprintf("DF2970: ldisc_deregister(6) returned void while a tty "
			"is OPEN on disc 6 (no use-count gate); linesw[6]."
			"l_open now %p (nodisc)\n", linesw[6].l_open);
		break;
	case 3:
		kprintf("DF2970: fake counts: l_open=%d l_close=%d "
			"(discipline was deregistered while in use => its "
			"l_close never ran; per-tty softc never released)\n",
			opens_a, closes_a);
		linesw[6] = saved6;
		kprintf("DF2970: restored linesw[6] l_open=%p\n",
			linesw[6].l_open);
		break;
	}
	return (0);
}

/* DF-2971: torn whole-struct swap of linesw entry */
static volatile int t2971_run;
static long torn_reads, tot_reads, a_reads, b_reads;
static struct thread *t2971_reg, *t2971_sam;

static void
t2971_register_thread(void *arg)
{
	while (t2971_run) {
		ldisc_register(8, &fakeA);
		ldisc_register(8, &fakeB);
	}
	kthread_exit();
}

static void
t2971_sample_thread(void *arg)
{
	struct linesw snap;

	while (t2971_run) {
		/* NO tty_token — exactly like the real dispatch sites */
		snap = *(volatile struct linesw *)&linesw[8];
		tot_reads++;
		if (snap.l_open == fakeA.l_open && snap.l_close == fakeA.l_close &&
		    snap.l_read == fakeA.l_read && snap.l_write == fakeA.l_write &&
		    snap.l_ioctl == fakeA.l_ioctl)
			a_reads++;
		else if (snap.l_open == fakeB.l_open && snap.l_close == fakeB.l_close &&
		    snap.l_read == fakeB.l_read && snap.l_write == fakeB.l_write &&
		    snap.l_ioctl == fakeB.l_ioctl)
			b_reads++;
		else
			torn_reads++;
	}
	kthread_exit();
}

static int
dfv_t2971(SYSCTL_HANDLER_ARGS)
{
	int error, go = 0;
	int dummy_t = 0;

	error = sysctl_handle_int(oidp, &go, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	if (go != 1)
		return (0);

	t2971_run = 1; torn_reads = tot_reads = a_reads = b_reads = 0;
	kthread_create(t2971_register_thread, NULL, &t2971_reg, "dfvtreg");
	kthread_create(t2971_sample_thread, NULL, &t2971_sam, "dfvtsam");
	tsleep(&dummy_t, 0, "dfvt", 4 * hz);
	t2971_run = 0;
	tsleep(&dummy_t, 0, "dfvt", hz);
	kprintf("DF2971: %ld snapshot reads: coherentA=%ld coherentB=%ld "
		"TORN(mixed old/new pointers)=%ld\n",
		tot_reads, a_reads, b_reads, torn_reads);
	ldisc_deregister(8);
	return (0);
}

/* ------------------------------------------------------------------ */
/* DF-2985: sysent torn publish                                        */
/* ------------------------------------------------------------------ */
static int dfv_nr = NO_SYSCALL;
static int s2985_registered;	/* only deregister what WE registered */
static struct sysent s2985_old;	/* entry captured at registration */
static int go2985;
static volatile int s2985_run;
static struct thread *s2985_thr;
static long torn_hits;

static int
f2985_a(struct sysmsg *sm, const void *args)
{
	const register_t *a = args;
	sm->sysmsg_result = a[1];	/* only meaningful if 2 args copied */
	return (0);
}
static int
f2985_b(struct sysmsg *sm, const void *args)
{
	sm->sysmsg_result = 0xB000;	/* sentinel */
	return (0);
}

static struct sysent e2985_a = { .sy_narg = 1, .sy_rsize = 0,
	.sy_call = (sy_call_t *)f2985_a, .sy_abort = NULL };
static struct sysent e2985_b = { .sy_narg = 2, .sy_rsize = 0,
	.sy_call = (sy_call_t *)f2985_b, .sy_abort = NULL };

static void
s2985_thread(void *arg)
{
	struct sysent old;
	int nr = dfv_nr;

	while (s2985_run) {
		syscall_register(&nr, &e2985_a, &old);
		tsleep(&go2985, 0, "dfvf1", 1);
		syscall_deregister(&nr, &old);
		syscall_register(&nr, &e2985_b, &old);
		tsleep(&go2985, 0, "dfvf1", 1);
		syscall_deregister(&nr, &old);
	}
	kthread_exit();
}

static int
dfv_s2985_start(SYSCTL_HANDLER_ARGS)
{
	struct sysent old;
	int error, go = 0;
	int dummy_t = 0;

	error = sysctl_handle_int(oidp, &go, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	if (go == 1 && dfv_nr == NO_SYSCALL) {
		torn_hits = 0;
		syscall_register(&dfv_nr, &e2985_a, &old);
		s2985_old = old;
		s2985_registered = 1;
		kprintf("DF2985: registered probe syscall nr=%d; flipper "
			"starting\n", dfv_nr);
		s2985_run = 1;
		kthread_create(s2985_thread, NULL, &s2985_thr, "dfvsyf");
	} else if (go == 3 && dfv_nr == NO_SYSCALL) {
		/* calibration: static variant A only, no flipping */
		syscall_register(&dfv_nr, &e2985_a, &old);
		s2985_old = old;
		s2985_registered = 1;
		kprintf("DF2985: CALIBRATION mode: static A registered at %d "
			"(measure baseline of ret==rand2)\n", dfv_nr);
	} else if (go == 2) {
		int nr2;
		s2985_run = 0;
		tsleep(&dummy_t, 0, "dfvsy", hz);
		nr2 = dfv_nr;
		if (s2985_registered) {
			if (sysent[nr2].sy_call == (sy_call_t *)f2985_a ||
			    sysent[nr2].sy_call == (sy_call_t *)f2985_b) {
				syscall_deregister(&nr2, &s2985_old);
			} else {
				/* slot already back to lkmnosys */
				syscall_register(&nr2, &e2985_a, &old);
				syscall_deregister(&nr2, &old);
			}
			s2985_registered = 0;
		}
		dfv_nr = NO_SYSCALL;
		kprintf("DF2985: flipper stopped, syscall slot cleaned\n");
	}
	return (0);
}

static int
dfv_s2985_nr(SYSCTL_HANDLER_ARGS)
{
	int error, nr = dfv_nr;
	error = sysctl_handle_int(oidp, &nr, 0, req);
	return (error);
}

/* ------------------------------------------------------------------ */
/* DF-2988: RB_SCAN concurrent-delete UAF                              */
/* ------------------------------------------------------------------ */
struct tst {
	RB_ENTRY(tst) link;
	int key;
	char pad[56];
};
static int tst_cmp(struct tst *a, struct tst *b);
RB_HEAD(th, tst);
RB_PROTOTYPE(th, tst, link, tst_cmp);
RB_GENERATE(th, tst, link, tst_cmp);

static int
tst_cmp(struct tst *a, struct tst *b)
{
	return (a->key - b->key);
}

static struct th dfv_tree;
static struct tst *victim;
static volatile int rb_arm, rb_go;
static int uaf_seen;
static struct thread *rb_del;

#define VKEY 311

static void *rb_pre_l, *rb_pre_r, *rb_pre_p;

static int
rb_cb(struct tst *n, void *x)
{
	if (n->key == VKEY && rb_go) {
		rb_pre_l = n->link.rbe_left;
		rb_pre_r = n->link.rbe_right;
		rb_pre_p = n->link.rbe_parent;
		kprintf("DF2988: scanner parked in callback holding node %p "
			"(key=%d) as its CURRENT element\n", n, n->key);
		rb_arm = 1;
		wakeup(&rb_del);
		tsleep(&rb_go, 0, "rbpark", 2 * hz); /* park inside callback */
		kprintf("DF2988: scanner resumed; re-reading freed node %p: "
			"key=%d (UAF read after concurrent RB_REMOVE+kfree)\n",
			n, n->key);
		/* the OTHER thread deleted+freed n while we were parked */
		if (n->key == 0x5a5a5a5a) {
			uaf_seen = 1;
			kprintf("DF2988: UAF READ CONFIRMED via reclaimer "
				"marker at node %p\n", n);
		}
		if (n->link.rbe_left != rb_pre_l ||
		    n->link.rbe_right != rb_pre_r ||
		    n->link.rbe_parent != rb_pre_p || n->key == 0x5a5a5a5a) {
			uaf_seen = 1;
			kprintf("DF2988: UAF CONFIRMED: scan held node %p as "
				"its current element across a concurrent "
				"RB_REMOVE+ kfree; post-wake reads observe "
				"mutated linkage (l %p->%p r %p->%p p %p->%p) "
				"- the header contract 'any item may be "
				"deleted during the scan' is NOT honored for "
				"another scanner's current element\n",
				n, rb_pre_l, n->link.rbe_left,
				rb_pre_r, n->link.rbe_right,
				rb_pre_p, n->link.rbe_parent);
		}
	}
	return (0);
}

static void
rb_deleter(void *arg)
{
	struct tst *re;

	while (!rb_arm)
		tsleep(&rb_del, 0, "rbdel", hz);
	RB_REMOVE(th, &dfv_tree, victim);
	kfree(victim, M_TEMP);
	(void)re;
	kprintf("DF2988: deleter (a DIFFERENT concurrent scanner) "
		"RB_REMOVE'd + kfree'd victim %p while the other scan holds "
		"it as current element - NO fixup occurs (RB_SCAN_INFO "
		"tracks only 'node', tree.h:302-306)\n", victim);
	wakeup(&rb_go);
	kthread_exit();
}

static int
dfv_rb2988(SYSCTL_HANDLER_ARGS)
{
	struct tst *nodes[8];
	int i, error, go = 0;

	error = sysctl_handle_int(oidp, &go, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	if (go != 1)
		return (0);

	RB_INIT(&dfv_tree);
	rb_arm = rb_go = 0;
	uaf_seen = 0;
	victim = NULL;
	for (i = 0; i < 8; i++) {
		nodes[i] = kmalloc(sizeof(struct tst), M_TEMP, M_WAITOK|M_ZERO);
		nodes[i]->key = 100 + i * 100;
		if (nodes[i]->key == VKEY) {
			kfree(nodes[i], M_TEMP);
			nodes[i] = NULL;
			continue;
		}
		RB_INSERT(th, &dfv_tree, nodes[i]);
	}
	victim = kmalloc(sizeof(struct tst), M_TEMP, M_WAITOK | M_ZERO);
	victim->key = VKEY;
	RB_INSERT(th, &dfv_tree, victim);

	rb_go = 1;
	kthread_create(rb_deleter, NULL, &rb_del, "dfvrb");
	RB_SCAN(th, &dfv_tree, NULL, rb_cb, NULL);
	rb_go = 0;
	tsleep(&error, 0, "rbx", hz);

	if (!uaf_seen)
		kprintf("DF2988: marker not observed this run\n");
	/* cleanup: everything except victim (freed) */
	for (i = 0; i < 8; i++)
		if (nodes[i])
			kfree(nodes[i], M_TEMP);
	return (0);
}

/* ------------------------------------------------------------------ */
/* DF-3001: hammer_vop_setattr applies atime/mtime with no ownership    */
/* check — probe with a non-owner fake cred at the VOP layer (the exact */
/* layer the NFS server drives). UFS control path must return EPERM.    */
/* ------------------------------------------------------------------ */

static int
dfv_sa3001_one(const char *path)
{
	struct nlookupdata nd;
	struct vnode *vp;
	struct vattr va0, va;
	struct ucred *cr;
	struct timespec ts = { 1234567890, 0 };
	int error;

	cr = crdup(curthread->td_ucred);
	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = 32767;	/* nobody */
	cr->cr_gid = cr->cr_rgid = cr->cr_svgid = 32767;

	error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
	if (error == 0)
		error = nlookup(&nd);
	if (error == 0)
		error = cache_vref(&nd.nl_nch, cr, &vp);
	nlookup_done(&nd);
	if (error) {
		kprintf("DF3001: %s: lookup error %d\n", path, error);
		crfree(cr);
		return (error);
	}
	error = vget(vp, LK_EXCLUSIVE);
	if (error) {
		vrele(vp); crfree(cr);
		return (error);
	}
	VOP_GETATTR(vp, &va0);
	VATTR_NULL(&va);
	va.va_atime = ts;
	va.va_mtime = ts;
	va.va_vaflags = 0;			/* plain set, not UTIMES_NULL */
	error = VOP_SETATTR(vp, &va, cr);
	VOP_GETATTR(vp, &va);
	vput(vp);
	crfree(cr);
	kprintf("DF3001: %s: VOP_SETATTR(atime/mtime, non-owner cred) -> %d; "
		"mtime now %ld (was %ld) %s\n", path, error,
		(long)va.va_mtime.tv_sec, (long)va0.va_mtime.tv_sec,
		(error == 0 && va.va_mtime.tv_sec == 1234567890) ?
		"*** TIMESTAMPS FORGED BY NON-OWNER ***" : "rejected");
	return (error);
}

static int
dfv_sa3001(SYSCTL_HANDLER_ARGS)
{
	int error, go = 0;

	error = sysctl_handle_int(oidp, &go, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	if (go != 1)
		return (0);
	dfv_sa3001_one("/mnt/h1x/ownerfile");	/* HAMMER1 probe */
	dfv_sa3001_one("/mnt/uctl/ownerfile");	/* UFS control  */
	return (0);
}

/* ------------------------------------------------------------------ */
static SYSCTL_NODE(, OID_AUTO, dfv, CTLFLAG_RW, 0, "DF verify rig");

SYSCTL_PROC(_dfv, OID_AUTO, q2889, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_q2889, "I", "DF-2889 color queue predicate");
SYSCTL_PROC(_dfv, OID_AUTO, cg2890, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_cg2890, "I", "DF-2890 contig OOB replica");
SYSCTL_PROC(_dfv, OID_AUTO, mc2907, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_mc2907, "I", "DF-2907 mchain safe");
SYSCTL_PROC(_dfv, OID_AUTO, l2970, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_l2970, "I", "DF-2970 linesw phases");
SYSCTL_PROC(_dfv, OID_AUTO, t2971, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_t2971, "I", "DF-2971 torn linesw");
SYSCTL_PROC(_dfv, OID_AUTO, s2985, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_s2985_start, "I", "DF-2985 sysent flip 1=start 2=stop");
SYSCTL_PROC(_dfv, OID_AUTO, nr2985, CTLTYPE_INT|CTLFLAG_RD, 0, 0,
    dfv_s2985_nr, "I", "DF-2985 probe syscall number");
SYSCTL_PROC(_dfv, OID_AUTO, rb2988, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_rb2988, "I", "DF-2988 RB_SCAN UAF");
SYSCTL_PROC(_dfv, OID_AUTO, sa3001, CTLTYPE_INT|CTLFLAG_RW, 0, 0,
    dfv_sa3001, "I", "DF-3001 setattr non-owner probe");

static int
dfv_safe_modevent(module_t mod, int type, void *data)
{
	switch (type) {
	case MOD_LOAD:
		kprintf("dfv_safe loaded\n");
		return (0);
	case MOD_UNLOAD:
		if (s2985_run) {
			kprintf("dfv_safe: flipper still running\n");
			return (EBUSY);
		}
		kprintf("dfv_safe unloaded\n");
		return (0);
	}
	return (EOPNOTSUPP);
}

static moduledata_t dfv_safe_mod = { "dfv_safe", dfv_safe_modevent, 0 };
DECLARE_MODULE(dfv_safe, dfv_safe_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
