DF-5773 / run5773.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | #!/bin/sh # DF-5773 run driver v3 (on the guest, as root). # usage: run5773.sh [delay_secs=0.10] # # The monitor installs handle_term AFTER fork() (init.c:206-218), so a # FIRST-boot child has SIGTERM=SIG_DFL. But after a reboot cycle # (cpu_reset() exit(EX_VKERNEL_REBOOT) -> monitor re-loops -> fork #2), # child #2 INHERITS handle_term, and in the child childpid==0, so # handle_term's kill(childpid, sig) (init.c:505) becomes kill(0, SIGTERM): # a signal to the ENTIRE PROCESS GROUP. Delivered while SIGTERM is # self-masked inside its own handler, it re-arms itself forever # (handler -> kill(0) -> pending TERM on self -> sigreturn -> redelivery). # # Sequence: boot vkroot to single-user, run `reboot` inside the guest # (clean cpu_reset -> monitor refork), then fire one SIGTERM during # child #2's early-boot window (fork -> SI_BOOT2_PROC0 initshutdown). # # EXPECTED (vulnerable): log frozen before "Mounting root" of the SECOND # boot, NO "Caught SIGTERM", kernel.debug alive at ~100% CPU, second TERM # useless, and the group splash: the runner's `sleep` sibling is TERMed # dead (kill(0) hit the whole pgrp) and the runner itself receives TERM # (trapped+logged). DELAY=${1:-0.10} VK=/usr/obj/usr/src/sys/VKERNEL64/kernel.debug cd /usr/obj/usr/src/sys/VKERNEL64 pkill -9 -f kernel.debug 2>/dev/null; sleep 1 rm -f /tmp/vkin /tmp/vk5773.log /tmp/vk5773.obs mkfifo /tmp/vkin (sleep 900 > /tmp/vkin) & HOLDER=$! sleep 300 & SLEEPER=$! trap 'echo "RUNNER_GOT_SIGTERM $(date +%s)" >> /tmp/vk5773.obs' TERM ./kernel.debug -m 512m -r /vkroot.img -n 2 -e init_path=/bin/vkinit -s \ < /tmp/vkin > /tmp/vk5773.log 2>&1 & VKPID=$! echo "RUN monitor pid=$VKPID sleeper=$SLEEPER delay=$DELAY" > /tmp/vk5773.obs # now that the vkernel holds the read end, open ours and answer the # mountroot prompt immediately (buffered input, DF-5661 rig trick) exec 3>/tmp/vkin echo "ufs:vkd0" >&3 # phase 1a: first boot reaches the vkinit single-user shell i=0 while [ $i -lt 60 ]; do grep -q "can't access tty" /tmp/vk5773.log 2>/dev/null && break kill -0 $VKPID 2>/dev/null || break sleep 1 i=$((i+1)) done echo "PHASE1 shell up i=$i" >> /tmp/vk5773.obs sleep 2 # phase 1b: reboot(2) from process context inside the guest -> # kern_reboot -> cpu_reset() exit(32) -> monitor refork -> # child #2 INHERITS handle_term (sigactions ran in the monitor loop after # fork #1; init.c:206-218 + 505) echo "/bin/reboot2" >&3 i=0 while [ $i -lt 1200 ]; do n=$(grep -ac "Using anonymous memory" /tmp/vk5773.log 2>/dev/null) [ "${n:-0}" -ge 2 ] && break kill -0 $VKPID 2>/dev/null || { echo "VK_DIED_BEFORE_REFORK" >> /tmp/vk5773.obs; break; } sleep 0.05 i=$((i+1)) done echo "PHASE1 refork n=$n" >> /tmp/vk5773.obs # phase 2: TERM burst into child #2's early-boot window # (fork -> SI_BOOT2_PROC0 initshutdown replaces the handler) k=0 while [ $k -lt 20 ]; do kill -TERM $VKPID 2>/dev/null sleep 0.05 k=$((k+1)) done echo "FIRING TERM burst (20 x 0.05s)" >> /tmp/vk5773.obs # snapshot marker counts before the burst's effects settle pre_caught=$(grep -ac "Caught SIGTERM" /tmp/vk5773.log 2>/dev/null) pre_mount=$(grep -ac "Mounting root" /tmp/vk5773.log 2>/dev/null) pre_anon=$(grep -ac "Using anonymous memory" /tmp/vk5773.log 2>/dev/null) i=0 while [ $i -lt 20 ]; do sleep 1 post_caught=$(grep -ac "Caught SIGTERM" /tmp/vk5773.log 2>/dev/null) kill -0 $VKPID 2>/dev/null || { echo "VK_DEAD" >> /tmp/vk5773.obs; break; } i=$((i+1)) done sleep 1 post_caught=$(grep -ac "Caught SIGTERM" /tmp/vk5773.log 2>/dev/null) post_mount=$(grep -ac "Mounting root" /tmp/vk5773.log 2>/dev/null) post_anon=$(grep -ac "Using anonymous memory" /tmp/vk5773.log 2>/dev/null) echo "COUNTS anon pre=$pre_anon post=$post_anon mount pre=$pre_mount post=$post_mount caught pre=$pre_caught post=$post_caught" >> /tmp/vk5773.obs if kill -0 $SLEEPER 2>/dev/null; then echo "SLEEPER_ALIVE (no group splash)" >> /tmp/vk5773.obs else echo "SLEEPER_DEAD_BY_GROUP_SPLASH" >> /tmp/vk5773.obs fi kill -TERM $VKPID 2>/dev/null && echo "SECOND_TERM_SENT" >> /tmp/vk5773.obs sleep 2 kill -0 $VKPID 2>/dev/null && echo "STILL_ALIVE_AFTER_SECOND_TERM(wedged)" >> /tmp/vk5773.obs || echo "DIED" >> /tmp/vk5773.obs echo "=== CPU state ===" >> /tmp/vk5773.obs ps aux | grep -a "[k]ernel.debug" >> /tmp/vk5773.obs 2>/dev/null echo "(end)" >> /tmp/vk5773.obs kill $HOLDER 2>/dev/null kill -9 $SLEEPER 2>/dev/null pkill -9 -f kernel.debug 2>/dev/null exec 3>&- cp /tmp/vk5773.log /root/vk5773.last.log 2>/dev/null exit 0 |