#!/bin/sh
# DF-4922 — dm_target_flakey_init failure paths leak the dm_pdev reference
# (and the open device vnode) taken by dm_pdev_insert() at
# sys/dev/disk/dm/flakey/dm_target_flakey.c:86.
#
# All three post-insert failure paths exercised, one vn unit each:
#   A. interval sum == 0 : "flakey /dev/vn0 0 0 0"              (:96-100)
#   B. unknown feature   : "flakey /dev/vn1 0 1 1 1 nosuchfeat" (:195-196)
#   C. interval overflow : "flakey /dev/vn2 0 2147483647 2147483647" (:102-106)
# Control: vn3 attached+detached with NO dm load -> must detach cleanly.
#
# Success criteria:
#   - every failed load returns EINVAL ("Invalid argument")
#   - M_DM malloc zone grows ~ +3*sizeof(dm_pdev_t) and STAYS grown
#     after the half-created dm devices are removed
#   - vn0/vn1/vn2 detach -> EBUSY ("Device busy") forever
#   - control vn3 detaches OK

exec 2>&1
echo "=== DF-4922 PoC (clean run) ==="
uname -a
kldload dm_target_flakey && echo "flakey module loaded"

for i in 0 1 2 3; do
	dd if=/dev/zero of=/tmp/back$i.img bs=1048576 count=1 2>/dev/null
done

echo "--- M_DM baseline ---"
vmstat -m | grep -w dm

echo "--- control: vn3 attach+detach, NO dm load ---"
vnconfig -c -S 1m -T vn3 /tmp/back3.img && echo "CONTROL attach vn3: OK"
if vnconfig -u vn3 2>/dev/null; then echo "CONTROL detach vn3: OK (expected)"; else echo "CONTROL detach vn3: FAILED (unexpected)"; fi

echo "--- path A: up_int+down_int == 0 ---"
vnconfig -c -S 1m -T vn0 /tmp/back0.img && echo "attach vn0 OK"
out=$(dmsetup create leakA --table "0 8 flakey /dev/vn0 0 0 0" 2>&1); rc=$?
echo "load rc=$rc out='$out' (expect Invalid argument)"

echo "--- path B: unknown feature ---"
vnconfig -c -S 1m -T vn1 /tmp/back1.img && echo "attach vn1 OK"
out=$(dmsetup create leakB --table "0 8 flakey /dev/vn1 0 1 1 1 nosuchfeature" 2>&1); rc=$?
echo "load rc=$rc out='$out' (expect Invalid argument)"

echo "--- path C: interval overflow ---"
vnconfig -c -S 1m -T vn2 /tmp/back2.img && echo "attach vn2 OK"
out=$(dmsetup create leakC --table "0 8 flakey /dev/vn2 0 2147483647 2147483647" 2>&1); rc=$?
echo "load rc=$rc out='$out' (expect Invalid argument)"

echo "--- M_DM after A+B+C (3 leaked pdevs expected) ---"
vmstat -m | grep -w dm

echo "--- remove half-created dm devices ---"
for d in $(dmsetup ls 2>/dev/null | awk '{print $1}'); do
	echo "removing dm device $d"; dmsetup remove "$d" >/dev/null 2>&1
done
echo "--- M_DM after dm removal (must NOT shrink: pdev refs leaked) ---"
vmstat -m | grep -w dm

echo "--- detach leaked units vs control ---"
for u in vn0 vn1 vn2; do
	if vnconfig -u $u 2>/tmp/err.$u; then
		echo "LEAK-UNIT $u detached OK (unexpected - no leak?)"
	else
		echo "LEAK-UNIT $u: $(cat /tmp/err.$u | tail -1)  <-- pdev ref leaked, device pinned"
	fi
done
vnconfig -c -S 1m -T vn3 /tmp/back3.img >/dev/null 2>&1
vnconfig -u vn3 >/dev/null 2>&1 && echo "CONTROL vn3 re-attach+detach: OK (proves EBUSY above is the leak, not the tool)"
echo "=== DF-4922 PoC end ==="
