#!/bin/bash
# POC runner: ANGLE struct-of-samplers equality → invalid SPIR-V → GPU-process crash
#
# Usage: CHROME_SRC=/path/to/chromium/src OUT_DIR=out/asan ./run.sh
set -u

CHROME_SRC=${CHROME_SRC:-/chromium/chromium/src}
OUT_DIR=${OUT_DIR:-out/asan}
CHROME=${CHROME_SRC}/${OUT_DIR}/chrome
SYM=${CHROME_SRC}/third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer
PORT=${PORT:-8080}
ARTIFACTS=$(cd "$(dirname "$0")" && pwd)

# 1. start http server
python3 "${ARTIFACTS}/server.py" ${PORT} >/tmp/poc-server.log 2>&1 &
SRV=$!
trap 'kill ${SRV} 2>/dev/null' EXIT
for i in $(seq 1 20); do
  curl -sf "http://127.0.0.1:${PORT}/poc.html" >/dev/null 2>&1 && break
  sleep 0.2
done
if ! curl -sf "http://127.0.0.1:${PORT}/poc.html" >/dev/null 2>&1; then
  echo "ERROR: server failed to start on port ${PORT}:" >&2
  cat /tmp/poc-server.log >&2
  exit 1
fi

# 2. gpu-launcher wrapper to capture /proc/PID/maps so we can symbolise
#    frames in libGLESv2.so / libvk_swiftshader.so (chrome's stack dumper
#    only symbolises the main binary).
LAUNCHER=/tmp/poc-gpu-launcher.sh
cat > "${LAUNCHER}" <<'EOF'
#!/bin/bash
"$@" &
PID=$!
# only capture maps for the *first* GPU process (the one that crashes)
if [[ ! -e /tmp/poc-gpu.maps ]]; then
  for i in $(seq 1 50); do
    if grep -q libvk_swiftshader /proc/$PID/maps 2>/dev/null; then
      cp /proc/$PID/maps /tmp/poc-gpu.maps 2>/dev/null
      break
    fi
    sleep 0.1
  done
fi
wait $PID
EOF
chmod +x "${LAUNCHER}"
rm -f /tmp/poc-gpu.maps

# 3. run chrome on the ANGLE-on-Vulkan path.
#    --use-angle=swiftshader selects the production ANGLE Vulkan backend
#    (the same TranslatorSPIRV code path that is the *default* WebGL2 backend
#    on Android) and supplies SwiftShader as the Vulkan ICD on this Linux box.
#    No security gate is bypassed by this flag — it only selects which Vulkan
#    driver receives the (unmodified, invalid) SPIR-V emitted by ANGLE.
PROFILE=/tmp/chrome-poc-profile
rm -rf "${PROFILE}"
LOG=/tmp/poc-chrome.log
timeout 60 "${CHROME}" \
  --headless=new \
  --no-sandbox --disable-gpu-sandbox \
  --no-first-run --no-default-browser-check \
  --disable-crash-reporter --disable-breakpad \
  --gpu-launcher="${LAUNCHER}" \
  --use-gl=angle --use-angle=swiftshader --enable-unsafe-swiftshader \
  --enable-logging=stderr --v=0 \
  --user-data-dir="${PROFILE}" \
  --virtual-time-budget=15000 \
  --dump-dom \
  "http://127.0.0.1:${PORT}/poc.html" > "${LOG}" 2>&1

# 4. symbolise. chrome offsets are already printed; only the <unknown> frames
#    (ANGLE libGLESv2.so / libvk_swiftshader.so) need manual mapping.
echo "================================================================"
echo "Relevant output (filtered)"
echo "================================================================"
grep -aE 'SPIR-V|signal 11|SEGV|ANGLE|CONSOLE|compile status|link status|RENDERER|gpu_process_host' "${LOG}" \
  | grep -avE 'dbus|inotify|dbind|at-spi|OAuth'

echo
echo "================================================================"
echo "Symbolised crash stack (GPU process)"
echo "================================================================"

lib_base() {
  awk -v p="$1" '$2=="r--p" && $3=="00000000" && $6==p {split($1,a,"-"); print "0x"a[1]; exit}' /tmp/poc-gpu.maps 2>/dev/null
}
SW=$(lib_base "${CHROME_SRC}/${OUT_DIR}/libvk_swiftshader.so")
GL=$(lib_base "${CHROME_SRC}/${OUT_DIR}/libGLESv2.so")

grep -aE '^#[0-9]+ ' "${LOG}" | while read -r idx addr rest; do
  if [[ "$rest" == *"chrome+"* ]]; then
    off=${rest##*chrome+}; off=${off%)}
    sym=$("$SYM" --obj="${CHROME}" "$off" 2>/dev/null | head -2 | paste -sd' ')
    printf '%-4s %s chrome+%s  %s\n' "$idx" "$addr" "$off" "$sym"
  elif [[ "$rest" == *"<unknown>"* ]]; then
    a=$((16#${addr#0x}))
    lib=""; off=""
    if [[ -n "$GL" ]] && (( a >= GL )) && (( a < GL + 0xc000000 )); then
      lib=libGLESv2.so; off=$(printf '0x%x' $((a - GL)))
    elif [[ -n "$SW" ]] && (( a >= SW )) && (( a < SW + 0x4000000 )); then
      lib=libvk_swiftshader.so; off=$(printf '0x%x' $((a - SW)))
    fi
    if [[ -n "$lib" ]]; then
      sym=$("$SYM" --obj="${CHROME_SRC}/${OUT_DIR}/${lib}" "$off" 2>/dev/null | head -2 | paste -sd' ')
      printf '%-4s %s %s+%s  %s\n' "$idx" "$addr" "$lib" "$off" "$sym"
    else
      printf '%-4s %s %s\n' "$idx" "$addr" "$rest"
    fi
  else
    printf '%-4s %s %s\n' "$idx" "$addr" "$rest"
  fi
done

echo
echo "(full raw log: ${LOG})"
