#!/bin/bash # Existing macOS Recovery/launchd runtime hook; never installs or erases anything. set -u PATH="/usr/bin:/bin:/usr/sbin:/sbin" export PATH PROOF_TOKEN="@@PROOF_TOKEN@@" STATE_DIR="/Volumes/installstate" PROOF_LOG="$STATE_DIR/proof.log" RESULT="$STATE_DIR/result.json" EXPECTED_BYTES=68719476736 MAX_LOG_BYTES=4194304 MAX_OUTPUT_BYTES=524288 TIMER_FIFO="/tmp/native-diagnostic-$PROOF_TOKEN-$$.fifo" PENDING_OUTPUTS=() ACTIVE_COMMAND="" ACTIVE_TIMER="" # BEGIN disk IPC diagnostic ACTIVE_OBSERVER="" # END disk IPC diagnostic os_version="" architecture="" uid=-1 system_exit=-1 arbitration_exit=-1 recovery_exit=-1 disk_list_exit=-1 selected_disk="" disk_bytes=0 count=0 while [ ! -d "$STATE_DIR" ] && (( count < 120 )); do /sbin/mount_9p installstate >/dev/null 2>&1 || : count=$((count + 1)) sleep 1 done [ -d "$STATE_DIR" ] || exit 1 : > "$PROOF_LOG" || exit 1 exec 3>> "$PROOF_LOG" || exit 1 rm -f "$RESULT" "$RESULT.tmp" printf '[proof-token] %s\n' "$PROOF_TOKEN" >&3 finish() { local success="$1" reason="$2" flush_outputs || { success=false; reason=diagnostic_log_budget_exceeded; } printf '[proof-result] %s: %s\n' "$success" "$reason" >&3 printf '{"token":"%s","success":%s,"reason":"%s","osVersion":"%s","architecture":"%s","uid":%s,"systemExit":%s,"diskArbitrationExit":%s,"recoveryExit":%s,"diskListExit":%s,"disk":"%s","diskBytes":%s,"readOnly":false}\n' \ "$PROOF_TOKEN" "$success" "$reason" "$os_version" "$architecture" "$uid" \ "$system_exit" "$arbitration_exit" "$recovery_exit" "$disk_list_exit" \ "$selected_disk" "$disk_bytes" > "$RESULT.tmp" /bin/mv -f "$RESULT.tmp" "$RESULT" || exit 1 exec 9>&- [ ! -p "$TIMER_FIFO" ] || /bin/rm -f "$TIMER_FIFO" # Keep the service alive for the bounded host diagnostic to capture evidence. while :; do sleep 60; done } init_timer_fifo() { # Recovery has Bash 3.2 before any SDK is installed. Its read timeout uses # alarm(), avoiding a separate sleep process for every command and grace period. [ ! -e "$TIMER_FIFO" ] || exit 1 /usr/bin/mkfifo -m 600 "$TIMER_FIFO" || exit 1 exec 9<> "$TIMER_FIFO" || exit 1 } flush_outputs() { (( ${#PENDING_OUTPUTS[@]} > 0 )) || return 0 local started=$SECONDS sizes="/tmp/native-diagnostic-$$.sizes" proof_size output_size raw_size local raw_count=0 raw_valid=1 pending_count=${#PENDING_OUTPUTS[@]} local bounded="/tmp/native-diagnostic-$$.flush" # One bounded native copy per group, rather than tail/stat startup per command. # Keep native byte-oriented copying: Bash 3.2 read -n would read large outputs # one byte per system call. Small scalar reads below have a separate tight bound. /usr/bin/tail -c "$MAX_OUTPUT_BYTES" "${PENDING_OUTPUTS[@]}" > "$bounded" || return 1 /usr/bin/stat -f '%z' "$PROOF_LOG" "$bounded" "${PENDING_OUTPUTS[@]}" > "$sizes" || return 1 { IFS= read -r proof_size; IFS= read -r output_size while IFS= read -r raw_size; do raw_count=$((raw_count + 1)) [[ "$raw_size" =~ ^[0-9]+$ ]] && (( raw_size <= MAX_OUTPUT_BYTES )) || raw_valid=0 done } < "$sizes" PENDING_OUTPUTS=() [[ "$proof_size" =~ ^[0-9]+$ && "$output_size" =~ ^[0-9]+$ ]] || return 1 (( raw_valid == 1 && raw_count == pending_count )) || return 1 (( proof_size + output_size + 1024 <= MAX_LOG_BYTES )) || return 1 /bin/cat "$bounded" >&3 || return 1 printf '\n[proof-flush] outputs-bytes=%s elapsed=%ss\n' "$output_size" "$((SECONDS - started))" >&3 } read_scalar() { local value status # All three values are short native machine/uid/version scalars. Reject excess # content instead of accepting a truncated first line as a successful gate. IFS= read -r -n 65 -d '' value < "$LAST_OUTPUT"; status=$? # EOF is mandatory: the byte bound or a NUL delimiter must never hide a suffix. (( status == 1 && ${#value} < 65 )) || return 1 value=${value%$'\n'} [[ "$value" != *$'\n'* ]] || return 1 SCALAR="$value" } # BEGIN successful sw_vers version parser read_product_version() { local value status line version="" fields=0 # Read the entire successful native output. EOF is mandatory; a NUL delimiter # or reaching the 1025-byte sentinel must never hide a suffix. LC_ALL=C IFS= read -r -n 1025 -d '' value < "$LAST_OUTPUT"; status=$? (( status == 1 && ${#value} <= 1024 )) || return 1 while IFS= read -r line || [ -n "$line" ]; do if [[ "$line" =~ ^[[:blank:]]*ProductVersion: ]]; then fields=$((fields + 1)) (( fields == 1 )) || return 1 [[ "$line" =~ ^[[:blank:]]*ProductVersion:[[:blank:]]*([0-9]+\.[0-9]+(\.[0-9]+)?)[[:blank:]]*$ ]] || return 1 version="${BASH_REMATCH[1]}" fi done <<< "$value" (( fields == 1 )) || return 1 SCALAR="$version" } # END successful sw_vers version parser # BEGIN disk IPC diagnostic # Optional observations have their own child/timer ownership. Thread state/time # targets only this probe's diskutil and does not request stack symbolication. observe_disk_query() { local disk_process="$1" output="$2" observation_child="" observation_timer="" cancel_observation() { trap '' TERM INT if [ -n "$observation_child" ]; then kill -TERM "$observation_child" 2>/dev/null || : IFS= read -r -t 2 -u 9 unused || : kill -KILL "$observation_child" 2>/dev/null || : wait "$observation_child" 2>/dev/null || : fi [ -z "$observation_timer" ] || { kill -TERM "$observation_timer" 2>/dev/null || :; wait "$observation_timer" 2>/dev/null || :; } printf '[disk-observation] stopped after the owned disk query\n' >> "$output" exit 143 } observe_command() { local name="$1" status started=$SECONDS shift printf '\n[disk-observation-command] %s:' "$name" >> "$output" printf ' %s' "$@" >> "$output" printf '\n' >> "$output" "$@" >> "$output" 2>&1 & observation_child=$! ( trap 'exit 0' TERM INT IFS= read -r -t 60 -u 9 unused || : printf '[disk-observation-timeout] %s child=%s limit=60s\n' "$name" "$observation_child" >> "$output" kill -TERM "$observation_child" 2>/dev/null || : IFS= read -r -t 2 -u 9 unused || : kill -KILL "$observation_child" 2>/dev/null || : ) & observation_timer=$! wait "$observation_child"; status=$? kill -TERM "$observation_timer" 2>/dev/null || : wait "$observation_timer" 2>/dev/null || : printf '[disk-observation-exit] %s status=%s elapsed=%ss\n' "$name" "$status" "$((SECONDS - started))" >> "$output" observation_child=""; observation_timer="" } trap cancel_observation TERM INT printf '[disk-observation] owned-diskutil-child=%s parent-shell=%s\n' "$disk_process" "$$" >> "$output" if [ -x /bin/ps ]; then if kill -0 "$disk_process" 2>/dev/null; then observe_command diskutil-threads /bin/ps -M -p "$disk_process" else printf '[disk-observation-unavailable] diskutil already exited before thread observation\n' >> "$output" fi else printf '[disk-observation-unavailable] /bin/ps is unavailable\n' >> "$output" fi } stop_disk_observation() { [ -n "$ACTIVE_OBSERVER" ] || return 0 kill -TERM "$ACTIVE_OBSERVER" 2>/dev/null || : wait "$ACTIVE_OBSERVER" 2>/dev/null || : ACTIVE_OBSERVER="" } # END disk IPC diagnostic cancel_probe() { trap '' TERM INT # BEGIN disk IPC diagnostic stop_disk_observation # END disk IPC diagnostic if [ -n "$ACTIVE_COMMAND" ]; then kill -TERM "$ACTIVE_COMMAND" 2>/dev/null || : IFS= read -r -t 2 -u 9 unused || : kill -KILL "$ACTIVE_COMMAND" 2>/dev/null || : wait "$ACTIVE_COMMAND" 2>/dev/null || : fi [ -z "$ACTIVE_TIMER" ] || { kill -TERM "$ACTIVE_TIMER" 2>/dev/null || :; wait "$ACTIVE_TIMER" 2>/dev/null || :; } ACTIVE_COMMAND=""; ACTIVE_TIMER="" finish false probe_cancelled } run_command() { local name="$1" shift local process timer exit_code started waited command_limit=45 # Run 4161: even native uname/ps startup took 34-42s under TCG. # Isolate only the failed UID gate; every other watchdog remains unchanged. [[ "$name" != uid ]] || command_limit=180 # BEGIN disk IPC diagnostic if [[ "$name" == disks && "${attempt:-0}" == 1 ]]; then command_limit=120; fi # END disk IPC diagnostic LAST_OUTPUT="/tmp/native-diagnostic-$name.out" printf '\n[proof-command] %s:' "$name" >&3 printf ' %s' "$@" >&3 printf '\n' >&3 started=$SECONDS "$@" > "$LAST_OUTPUT" 2>&1 & process=$! ACTIVE_COMMAND="$process" printf '[proof-start] %s child=%s shell=%s parent=%s seconds=%s\n' "$name" "$process" "$$" "$PPID" "$started" >&3 printf '[proof-limit] %s %ss\n' "$name" "$command_limit" >&3 ( trap 'exit 0' TERM INT IFS= read -r -t "$command_limit" -u 9 unused || : printf '[proof-timeout] %s child=%s elapsed=%ss signal=TERM\n' "$name" "$process" "$((SECONDS - started))" >&3 kill -TERM "$process" 2>/dev/null || : IFS= read -r -t 2 -u 9 unused || : kill -KILL "$process" 2>/dev/null || : ) & timer=$! ACTIVE_TIMER="$timer" # BEGIN disk IPC diagnostic if [[ "$name" == disks && "$attempt" == 1 ]]; then local observation_output="/tmp/native-diagnostic-disk-observation.out" : > "$observation_output" observe_disk_query "$process" "$observation_output" & ACTIVE_OBSERVER=$! printf '[disk-observation-start] observer=%s owned-diskutil-child=%s\n' "$ACTIVE_OBSERVER" "$process" >&3 PENDING_OUTPUTS+=("$observation_output") fi # END disk IPC diagnostic wait "$process" exit_code=$? waited=$SECONDS # Includes fork/exec/wait, but excludes timer cleanup and evidence copying. printf '[proof-native-wait] %s child=%s elapsed=%ss exit=%s\n' "$name" "$process" "$((waited - started))" "$exit_code" >&3 kill -TERM "$timer" 2>/dev/null || : wait "$timer" 2>/dev/null || : # BEGIN disk IPC diagnostic stop_disk_observation # END disk IPC diagnostic ACTIVE_COMMAND=""; ACTIVE_TIMER="" printf '[proof-cleanup] %s child=%s elapsed=%ss total=%ss\n' "$name" "$process" "$((SECONDS - waited))" "$((SECONDS - started))" >&3 printf '[proof-exit] %s\n' "$exit_code" >&3 LAST_EXIT="$exit_code" PENDING_OUTPUTS+=("$LAST_OUTPUT") return 0 } diagnose_failure() { run_command kernel /usr/bin/uname -a run_command account /usr/bin/id run_command context /usr/sbin/sysctl kern.bootargs machdep.cpu.brand_string machdep.cpu.features machdep.cpu.leaf7_features run_command parent /bin/ps -p "$$" -p "$PPID" -o pid=,ppid=,comm= run_command processes /bin/ps -axo pid,ppid,comm } fail_probe() { local reason="$1" flush_outputs || finish false diagnostic_log_budget_exceeded diagnose_failure finish false "$reason" } init_timer_fifo trap cancel_probe TERM INT # Test the required native gates before optional process/CPU diagnostics. run_command architecture /usr/bin/uname -m (( LAST_EXIT == 0 )) || fail_probe architecture_probe_failed read_scalar || fail_probe architecture_output_invalid architecture="$SCALAR" [ "$architecture" = x86_64 ] || fail_probe unexpected_guest_architecture run_command uid /usr/bin/id -u (( LAST_EXIT == 0 )) || fail_probe uid_probe_failed read_scalar || fail_probe uid_output_invalid uid="$SCALAR" [ "$uid" = 0 ] || fail_probe recovery_account_not_root run_command platform /usr/bin/sw_vers platform_exit="$LAST_EXIT" flush_outputs || finish false diagnostic_log_budget_exceeded if (( platform_exit != 0 )); then run_command system /bin/launchctl print system system_exit="$LAST_EXIT" run_command arbitration /bin/launchctl print system/com.apple.diskarbitrationd arbitration_exit="$LAST_EXIT" run_command recovery /bin/launchctl print system/com.apple.recoveryosd recovery_exit="$LAST_EXIT" printf '[proof-retry] sw_vers once after native service context; same 45-second deadline\n' >&3 run_command platform-warm /usr/bin/sw_vers platform_exit="$LAST_EXIT" fi (( platform_exit == 0 )) || fail_probe sw_vers_failed # BEGIN successful sw_vers version extraction read_product_version || fail_probe product_version_invalid # END successful sw_vers version extraction os_version="$SCALAR" [[ "$os_version" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]] || fail_probe product_version_invalid (( ${os_version%%.*} >= 14 )) || fail_probe unsupported_macos_version flush_outputs || finish false diagnostic_log_budget_exceeded # BEGIN disk IPC diagnostic run_command arbitration_before /bin/launchctl print system/com.apple.diskarbitrationd run_command management_before /bin/launchctl print system/com.apple.diskmanagementd run_command media_before /usr/sbin/ioreg -r -c IOMedia -l -w 0 flush_outputs || finish false diagnostic_log_budget_exceeded # END disk IPC diagnostic # Bound readiness independently of the host's 40-minute overall deadline. readiness_start=$SECONDS attempt=0 while (( attempt < 1 && SECONDS - readiness_start < 600 )); do attempt=$((attempt + 1)) printf '\n[readiness-attempt] %s\n' "$attempt" >&3 run_command disks /usr/sbin/diskutil list physical disk_list_exit="$LAST_EXIT" if (( disk_list_exit == 0 )); then disk_list=$(cat "$LAST_OUTPUT") candidates=0 while IFS= read -r disk; do [ -n "$disk" ] || continue run_command "info-$disk" /usr/sbin/diskutil info "/dev/$disk" (( LAST_EXIT == 0 )) || continue info=$(cat "$LAST_OUTPUT") if printf '%s\n' "$info" | grep -Eq '^[[:space:]]*(Read-Only (Media|Device)|(Media|Device) Read-Only):[[:space:]]*Yes'; then continue fi printf '%s\n' "$info" | grep -Eq '^[[:space:]]*(Read-Only (Media|Device)|(Media|Device) Read-Only):[[:space:]]*No' || continue size=$(printf '%s\n' "$info" | sed -nE 's/^[[:space:]]*Disk Size:.*\(([0-9]+) Bytes\).*/\1/p' | head -n 1) [[ "$size" =~ ^[0-9]+$ ]] || continue (( size == EXPECTED_BYTES )) || continue candidates=$((candidates + 1)) selected_disk="/dev/$disk" disk_bytes="$size" printf '[writable-target] %s %s bytes\n' "$selected_disk" "$disk_bytes" >&3 done < <(printf '%s\n' "$disk_list" | sed -nE 's#^/dev/(disk[0-9]+).*#\1#p') (( candidates <= 1 )) || fail_probe ambiguous_writable_64g_disks if (( candidates == 1 )); then # Re-probe live launchd domains after disk readiness, preserving native exits. run_command system_ready /bin/launchctl print system system_exit="$LAST_EXIT" run_command arbitration_ready /bin/launchctl print system/com.apple.diskarbitrationd arbitration_exit="$LAST_EXIT" run_command recovery_ready /bin/launchctl print system/com.apple.recoveryosd recovery_exit="$LAST_EXIT" (( system_exit == 0 && arbitration_exit == 0 && recovery_exit == 0 )) || fail_probe service_domain_not_ready finish true native_recovery_and_writable_64g_disk_ready fi fi flush_outputs || finish false diagnostic_log_budget_exceeded IFS= read -r -t 5 -u 9 unused || : done fail_probe disk_management_or_writable_target_not_ready