⬢ DragonFlyBSD Kernel Audit
DF-5667 / vk_probe4.sh
← back to finding ↓ download raw
#!/bin/sh
# DF-5667 probe driver v4 (canonical) — vkernel DDB has no symbol table
# (init.c kdb_init is #if 0'd) so DDB is driven with raw nm-derived
# addresses; the CPU_prvspace base pointer is parsed from an in-vkernel
# /dev/kmem od(1) read.  DDB ARGS MUST BE 0x-PREFIXED HEX (ddb input radix
# is 16 — decimal literals get reinterpreted as hex, which is what crashed
# probe v3 into pmap_kextract's KKASSERT).
# Proves:
#   (1) is_globaldata_space/kernacc return TRUE (1) for reserved-but-unmapped
#       per-CPU slots (dead slots beyond ncpus)
#   (2) what pmap_kextract() returns for a dead slot (PDE==0 path reads
#       guest-phys page 0 = bootstrap PML4 through the DMAP)
#   (3) /dev/kmem composed verdict (kvm_access_check) for a dead slot
#   (4) /dev/kmem behavior across the whole window (EFAULT boundary,
#       porous pages, content, or a vkernel panic)
set -u
VK=/usr/obj/usr/src/sys/VKERNEL64/kernel.debug
LOG=/tmp/vk5667.log
cd /usr/obj/usr/src/sys/VKERNEL64

pkill -9 -f kernel.debug 2>/dev/null; sleep 1
rm -f /tmp/vkin $LOG
mkfifo /tmp/vkin
(sleep 1500 > /tmp/vkin 2>/dev/null) </dev/null &
HOLDER=$!

nsym() { nm $VK | awk -v s="$1" '$3==s{print "0x"$1}' | head -1; }
SYM_PRV=$(nsym CPU_prvspace)
SYM_NCPUS=$(nsym ncpus)
F_KERNACC=$(nsym kernacc)
F_PKEX=$(nsym pmap_kextract)
F_IGDS=$(nsym is_globaldata_space)
F_KVAC=$(nsym kvm_access_check)
echo "DF5667 SYMS prv=$SYM_PRV ncpus=$SYM_NCPUS kernacc=$F_KERNACC pkex=$F_PKEX igds=$F_IGDS kvac=$F_KVAC"

( exec 3> /tmp/vkin; echo "ufs:vkd0" >&3 ) &

./kernel.debug -m 512m -r /vkroot.img -n 2 \
      -e init_path=/bin/vkinit \
      -s < /tmp/vkin 2>&1 | tee $LOG > /dev/console &

i=0
while [ $i -lt 150 ]; do
  grep -q "Mounting root from ufs:vkd0" $LOG 2>/dev/null && grep -q "^# " $LOG 2>/dev/null && break
  pgrep -f "kernel.debug -m 512m" >/dev/null 2>&1 || break
  sleep 1; i=$((i+1))
done
sleep 4
echo "=== DF5667 vkernel booted (i=$i) ==="

send() { ( exec 4> /tmp/vkin; echo "$1" >&4 ); }

# ---------- stage 1: read base pointer + ncpus via /dev/kmem -------------
send "echo DF5667_STAGE1"
send "dd if=/dev/kmem bs=8 count=1 skip=\$(( $SYM_PRV / 8 )) 2>/dev/null | od -An -tx1"
sleep 5
BYTES=$(grep -a -A1 "od -An" $LOG | tail -1 | tr -d '\r')
echo "DF5667 raw ptr bytes: [$BYTES]"
B10=$(printf '%s\n' "$BYTES" | awk '{
  v="0x";
  for (i=NF; i>=1; i--) v = v $i;
  print v }' )
B10=$((B10))
echo "DF5667 BASE16=$(printf 0x%x $B10) BASE10=$B10"

if [ "$B10" -lt 1000 ] 2>/dev/null; then
  echo "DF5667 ERROR: bad base parse, aborting probes"
  kill $HOLDER 2>/dev/null; pkill -9 -f kernel.debug 2>/dev/null
  exit 1
fi

DEAD=$((B10 + 0x20000 + 0x123))
DEADP=$((B10 + 0x20000))
LIVE=$((B10 + 0x40))
echo "DF5667 targets DEAD=$(printf 0x%x $DEAD) DEADP=$(printf 0x%x $DEADP) LIVE=$(printf 0x%x $LIVE)"

# ---------- stage 2: DDB call battery (0x-hex args: ddb radix is 16) -------
send "sysctl -w debug.enter_debugger=ddb"
sleep 4
DEAD16=$(printf '0x%x' $DEAD); DEAD816=$(printf '0x%x' $((DEAD+8)))
DEADP16=$(printf '0x%x' $DEADP); DEADPE16=$(printf '0x%x' $((DEADP+4096)))
LIVE16=$(printf '0x%x' $LIVE); LIVE816=$(printf '0x%x' $((LIVE+8)))
( exec 5> /tmp/vkin
  printf 'call %s(%s, %s)\n'     "$F_IGDS"    "$DEAD16" "$DEAD816" >&5; sleep 2
  printf 'call %s(%s, 8, 1)\n'   "$F_KERNACC" "$DEAD16" >&5;           sleep 2
  printf 'call %s(%s)\n'         "$F_PKEX"    "$DEAD16" >&5;           sleep 2
  printf 'call %s(%s, %s, 1)\n'  "$F_KVAC"    "$DEADP16" "$DEADPE16" >&5; sleep 2
  printf 'call %s(%s, 8, 1)\n'   "$F_KERNACC" "$LIVE16" >&5;           sleep 2
  printf 'call %s(%s)\n'         "$F_PKEX"    "$LIVE16" >&5;           sleep 2
  printf 'call %s(%s, %s)\n'     "$F_IGDS"    "$LIVE16" "$LIVE816" >&5; sleep 2 ) &
sleep 18
send "c"
sleep 3

# ---------- stage 3: /dev/kmem page scan (console transitions only) -------
send "echo DF5667_STAGE3 BASE=$B10"
send "n=0; prev=X; while [ \$n -lt 1700 ]; do off=\$(( $B10/4096 + n )); if dd if=/dev/kmem of=/dev/null bs=4096 count=1 skip=\$off 2>/dev/null; then st=OK; else st=EF; fi; if [ \"\$st\" != \"\$prev\" ]; then echo \"PG \$n \$st addr=\$((off*4096))\"; prev=\$st; fi; n=\$((n+1)); done; echo DF5667_SCAN_DONE"
sleep 170

# ---------- outcome --------------------------------------------------------
if pgrep -f "kernel.debug -m 512m" >/dev/null 2>&1; then ALIVE=yes; else ALIVE=no; fi
echo "DF5667 VKERNEL_ALIVE=$ALIVE"
echo "=== DDB call battery ==="
grep -an "^call\|= 0x\|0x8000" $LOG | head -40
echo "=== scan transitions ==="
grep -an "PG \|SCAN_DONE" $LOG | head -40
echo "=== tail ==="
tail -8 $LOG

if [ "$ALIVE" = yes ]; then
  send "echo DF5667_BYE; reboot"
  sleep 6
fi
kill $HOLDER 2>/dev/null
pkill -9 -f kernel.debug 2>/dev/null
echo "DF5667_DRIVER_DONE"