Cada arnés headless tenía su propio subconjunto de MOZ_DISABLE_*_SANDBOX, elegido en su momento sin medir: banco 1 de 5, perfilar 4, codecs 3, ruteo 3, nativo 2, inicio 2. Con cualquier subconjunto incompleto el sandbox de Firefox sigue intentando montar su user-namespace dentro del de bwrap, falla con 'uid_map: EPERM' y deja un ayudante 'Sandbox Forked' muerto de SIGSEGV por cada intento — en el 100% de las corridas, después de escribir el PNG y por eso invisible. Medido: 2 cadáveres por corrida con sólo CONTENT apagado, 0 con los cinco. Verificado sobre el arnés ya editado: segv=0 EPERM=0 screenshot=sí. Acá no se pierde seguridad: bwrap ya es la jaula, el sandbox de Gecko es redundante y lo único que hace dentro es fallar. ⚠ Cambia las condiciones de medición del banco de PGO: las cifras de docs/26 se tomaron con el arnés viejo. Los cocientes del PGO comparan dos firefox bajo el mismo arnés y deberían aguantar; la línea base absoluta de arranque no tiene por qué. Anotado en la cabecera. No se tocan scripts/wlr/dunst-headless.sh (otro agente trabajando en él) ni los cazadores, que conservan el entorno viejo a propósito para poder reproducir la condición original. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2
71 lines
3.9 KiB
Bash
Executable File
71 lines
3.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# perfilar.sh — corrida de entrenamiento PGO: recorre el corpus con el firefox INSTRUMENTADO y deja
|
|
# los `.profraw` en /salida. Pensado para correr dentro de un bwrap con:
|
|
# --ro-bind <rootfs> / --ro-bind <corpus> /pgo --bind <salida> /salida --unshare-pid
|
|
#
|
|
# ══ TRES DEFENSAS QUE NO SON PARANOIA: LAS TRES SE PAGARON ═════════════════════════════════════
|
|
#
|
|
# 1. `--unshare-pid` EN EL BWRAP (fuera de este script). Sin él los hijos SOBREVIVEN al sandbox: un
|
|
# `http.server` quedó vivo 19 HORAS, retuvo el puerto y envenenó las corridas siguientes.
|
|
#
|
|
# 2. PUERTO ALEATORIO POR CORRIDA. Elimina la colisión de raíz en vez de detectarla: si otro proceso
|
|
# tiene un puerto, este arranque usa otro y no se entera.
|
|
#
|
|
# 3. MARCADOR ÚNICO POR CORRIDA, servido desde una copia ESCRIBIBLE del corpus. La primera versión
|
|
# horneaba el marcador como constante del script, y eso rompía justo lo que el chequeo existe
|
|
# para probar: un servidor ajeno servía el MISMO fichero de marca y el chequeo lo daba por bueno.
|
|
# Verificaba «alguien sirve este contenido», no «este servidor es el mío».
|
|
#
|
|
# ⚠ Y LOS SANDBOXES DE FIREFOX VAN APAGADOS. Con ellos los procesos hijo mueren con SIGNAL 11, el
|
|
# que renderiza no llega a nacer, el servidor registra 0 GET y el único `.profraw` es el del padre
|
|
# arrancando: un perfil de nada, con todo en verde. Es lo mismo que hace el `profileserver.py` de
|
|
# Mozilla. Sólo aplica al entrenamiento; el firefox que se distribuye lleva sus sandboxes intactos.
|
|
set -u
|
|
export HOME=/tmp/home; mkdir -p "$HOME"
|
|
export LANG=C.UTF-8 MOZ_HEADLESS=1 LIBGL_ALWAYS_SOFTWARE=1
|
|
# Los CINCO sandboxes de Gecko apagados, no cuatro: el sandbox propio de Gecko no puede montar su user-namespace dentro de bwrap
|
|
# (uid_map: EPERM) y CADA intento fallido deja un ayudante 'Sandbox Forked' muerto de SIGSEGV.
|
|
# Medido 2026-09-08: 2 cadáveres por corrida con sólo CONTENT apagado, 0 con los cinco. Acá no
|
|
# se pierde nada: bwrap YA es la jaula. Ver scripts/diagnostico/cazar-cuelgue-headless.sh.
|
|
export MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_GMP_SANDBOX=1 MOZ_DISABLE_RDD_SANDBOX=1 MOZ_DISABLE_SOCKET_PROCESS_SANDBOX=1 MOZ_DISABLE_UTILITY_SANDBOX=1
|
|
export LLVM_PROFILE_FILE=/salida/ff-%p-%m.profraw
|
|
|
|
MARCA="pgo-$$-$(cat /proc/sys/kernel/random/uuid 2>/dev/null || echo ${RANDOM:-0}${RANDOM:-0})"
|
|
PUERTO=$(( 20000 + $$ % 20000 ))
|
|
|
|
# Copia ESCRIBIBLE del corpus (tmpfs): hace falta para poner el marcador dentro de lo que se sirve.
|
|
SERVIR=/tmp/servir; mkdir -p "$SERVIR"; cp -a /pgo/. "$SERVIR"/ 2>/dev/null
|
|
printf '%s\n' "$MARCA" > "$SERVIR/marca.txt"
|
|
|
|
cd "$SERVIR" && /usr/bin/python3 -m http.server "$PUERTO" --bind 127.0.0.1 > /salida/httpd.log 2>&1 &
|
|
|
|
# Espera CON REINTENTO (un `sleep` fijo concluía «no responde» sobre un servidor que sí iba a
|
|
# responder) y exigiendo el marcador de ESTA corrida.
|
|
i=0
|
|
while [ $i -lt 25 ]; do
|
|
got=$(/usr/bin/python3 -c "import urllib.request
|
|
try: print(urllib.request.urlopen('http://127.0.0.1:$PUERTO/marca.txt',timeout=3).read().decode().strip())
|
|
except Exception: pass" 2>/dev/null)
|
|
[ "$got" = "$MARCA" ] && break
|
|
i=$((i+1)); sleep 1
|
|
done
|
|
[ $i -lt 25 ] || {
|
|
echo "!! en $PUERTO no contesta MI servidor (marca leída: '${got:-nada}') — abortando antes de" >&2
|
|
echo " generar perfiles de páginas que nunca se cargaron." >&2
|
|
tail -3 /salida/httpd.log >&2
|
|
exit 1
|
|
}
|
|
echo "== servidor propio OK en $PUERTO"
|
|
|
|
n=0
|
|
for f in $(cd "$SERVIR" && find . -name '*.html' | sed 's|^\./||' | sort); do
|
|
n=$((n+1))
|
|
LD_LIBRARY_PATH=/usr/lib/firefox timeout 200 /usr/lib/firefox/firefox --headless \
|
|
--screenshot /salida/shot.png "http://127.0.0.1:$PUERTO/$f" >/dev/null 2>&1
|
|
printf '.'
|
|
done
|
|
echo ""
|
|
echo "== páginas recorridas: $n"
|
|
echo "== GETs servidos: $(grep -c GET /salida/httpd.log 2>/dev/null)"
|
|
echo "== .profraw: $(ls /salida/*.profraw 2>/dev/null | wc -l)"
|