#!/bin/sh
# DF-2801 run — root.  Floor-comparison design:
#   The leaked namecache reference (cache_copy at kern_jail.c:206, never
#   cache_drop'ed on the error paths) permanently pins one namecache entry
#   per failed jail().  Lazy reclaim confounds instantaneous deltas, so we
#   compare settled floors:
#     F0                        - settled baseline
#     control cycle -> F1       (jail fails BEFORE kern_jail: EFAULT at
#                                copyinstr; no cache_copy; floor unchanged)
#     leak cycle    -> F2       (jail fails AFTER cache_copy at kern_chroot
#                                ENOTDIR; floor +~iters forever)
#   "pressure" = create+delete 8000 junk files to force the cache to
#   reclaim everything it *can*.
set -e
[ "$(id -u)" = 0 ] || { echo "must run as root"; exit 2; }
D=/tmp/df2801

pressure() {
    mkdir -p /tmp/df2801press
    for i in $(jot 8000); do : > /tmp/df2801press/$i; done
    rm -rf /tmp/df2801press
}

settle() { pressure; sleep 5; }

settle; F0=$(sysctl -n vfs.cache.numcache); echo "F0 baseline floor        : $F0"

echo "=== CONTROL cycle (EFAULT before kern_jail — no cache_copy) ==="
$D/jailleak 2000 control >/dev/null 2>&1 || true
rm -f /tmp/df2801_*
settle; F1=$(sysctl -n vfs.cache.numcache); echo "F1 after control cycle   : $F1"

echo "=== LEAK cycle (ENOTDIR after cache_copy — ref leaked) ==="
$D/jailleak 2000 leak
rm -f /tmp/df2801_*
settle; F2=$(sysctl -n vfs.cache.numcache); echo "F2 after leak cycle      : $F2"

echo "control growth: $((F1 - F0))   leak growth: $((F2 - F1))"
if [ $((F2 - F1)) -gt 1000 ] && [ $((F1 - F0)) -lt 500 ]; then
    echo "REPRODUCED: ~$((F2 - F1)) namecache entries permanently pinned by 2000 failed jail(2) calls"
fi
