harkaq: Q2 RESUELTA — el runtime base son 5 paths, y aparece una 3ª categoría
Método (lo importante): NO se adivina, se mide. MODE=base corre SIN ninguna dep
declarada ⇒ sin clausura que pueda explicarlas, toda denegación es runtime base
por definición. Aísla la respuesta sin juicio de valor.
MODE=base, hello.c mínimo con zig cc: 18 denegaciones, 4 paths distintos
(contador del kernel 19 = 18 + canario ✓):
8× /dev/urandom 6× /usr/lib/os-release 3× /dev/null 1× /usr/bin/env
Y el build salió rc=0: no son cosas que el build NECESITE, son cosas que INTENTA
— pero salen en todos los builds y sin tratarlas entierran la deuda real.
La partición cierra en TRES categorías, todas medidas (§4.2):
- Contrato del sandbox: /src /out /tmp /dev/null(rw) /dev/urandom /proc
/opt/zig — los monta BWRAP, no son Alpine ⇒ no son deuda. Ésta era la línea
divisoria que faltaba.
- Runtime base Alpine: /bin/sh /bin/busybox /lib/ld-musl /usr/bin/env
/usr/lib/os-release. CINCO. ⇒ el resto de Alpine es deuda genuina, y eso
hace viable todo el planteo.
- Denegación ESPERADA (no estaba en el diseño): /usr/bin/gcc. zig cc lo sondea
3× y el build sale rc=0 igual ⇒ NO es ruido a permitir, es la jaula haciendo
su trabajo: concederla dejaría a zig invocar el gcc de Alpine por detrás,
justo lo que la campaña "matar gcc" persigue a mano. Es D3 literal ("una
denegación esperada no se calla, se CLASIFICA") y confirma que no tener
`quiet` era correcto: silenciarla habría borrado el hallazgo.
El contraste con el mismo runtime base:
MODE=base rc=0 Impuro [ /usr/bin/gcc ] ← esperada
MODE=zlib rc=1 Impuro [ /usr/lib/libz.so.1.3.2 ] ← DEUDA PURA, sin ruido
Consecuencia de diseño para el Verdict: las denegaciones tienen que venir
CLASIFICADAS (base|esperada|deuda), no en lista plana. `Hermetico` debe
significar "cero deuda", no "cero denegaciones" — si no, ningún build real lo
alcanzaría jamás.
Gotchas medidos: /dev/null es destino de ESCRITURA (rw, no ro) y el piso duro es
/bin/sh→busybox→ld-musl (sin eso ni el canario corre ⇒ sólo SinEvidencia).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -168,3 +168,33 @@ estado: Impuro | canario: True | contador kernel: 3
|
||||
|
||||
El segundo lo cazó **D9 mismo**: el lector se negó a certificar (`SinEvidencia`) en vez de decir
|
||||
`Hermetico`. El canario funcionando como se diseñó, sobre un bug de quien lo diseñó.
|
||||
|
||||
### Q2 resuelta: el runtime base son 5 paths (2026-07-15)
|
||||
|
||||
`MODE=base` corre **sin ninguna dep declarada**: sin clausura que pueda explicarlas, toda
|
||||
denegación es runtime base por definición. Eso aísla la respuesta sin juicio de valor.
|
||||
|
||||
```sh
|
||||
export BASE_EXTRA='ro /bin/sh
|
||||
ro /bin/busybox
|
||||
ro /lib/ld-musl-x86_64.so.1
|
||||
ro /usr/bin/env
|
||||
ro /usr/lib/os-release
|
||||
rw /dev/null
|
||||
ro /dev/urandom'
|
||||
MODE=base scripts/harkaq/q2-runtime-base.sh # rc=0, sólo queda /usr/bin/gcc (esperada)
|
||||
MODE=zlib scripts/harkaq/q2-runtime-base.sh # Impuro: [/usr/lib/libz.so.1.3.2] — deuda pura
|
||||
```
|
||||
|
||||
Tres categorías, todas medidas (detalle en `docs/16-harkaq-jaula.md` §4.2):
|
||||
|
||||
- **Contrato**: `/src /out /tmp /dev/null(rw) /dev/urandom /proc /opt/zig` — los monta bwrap, no
|
||||
son Alpine.
|
||||
- **Runtime base Alpine**: `/bin/sh /bin/busybox /lib/ld-musl /usr/bin/env /usr/lib/os-release`.
|
||||
Cinco. El resto de Alpine es deuda genuina.
|
||||
- **Denegación esperada**: `/usr/bin/gcc` — `zig cc` lo sondea y el build sale `rc=0` igual.
|
||||
Concederla dejaría a zig usar el gcc de Alpine por detrás. **Se deniega y se clasifica** (D3).
|
||||
|
||||
Ojo con dos: `/dev/null` es destino de **escritura** (`rw`, no `ro` — con `ro` quedan
|
||||
`fs.write_file` colgando), y el piso duro es `/bin/sh`→`busybox`→`ld-musl`: sin eso ni el canario
|
||||
corre y no hay diagnóstico, sólo `SinEvidencia`.
|
||||
|
||||
@@ -17,23 +17,43 @@ cd "$(dirname "$0")/../.."
|
||||
BIN="${HARKAQ_BIN:-$HOME/.cache/harkaq}"
|
||||
ROOTFS=.dev-fs/alpine
|
||||
ZIG=.dev-fs/tools/zig
|
||||
DEP=$(ls -d store/*-zlib | head -1)
|
||||
|
||||
# MODE=base → build SIN ninguna dep declarada. Por definición, TODA denegación es runtime base:
|
||||
# no hay clausura que pueda explicarla. Aísla la respuesta a Q2 sin juicio de valor.
|
||||
# MODE=zlib → el mismo build contra la dep zlib del store. Lo que sobre por encima del runtime
|
||||
# base ES la deuda de de-Alpinización, sin ambigüedad.
|
||||
MODE="${MODE:-zlib}"
|
||||
|
||||
[ -d "$ROOTFS" ] || { echo "falta $ROOTFS"; exit 1; }
|
||||
[ -d "$ZIG" ] || { echo "falta $ZIG"; exit 1; }
|
||||
|
||||
WORK=$(mktemp -d); trap 'rm -rf "$WORK"' EXIT
|
||||
mkdir -p "$WORK/src" "$WORK/out"
|
||||
cat > "$WORK/src/hello.c" <<'EOF'
|
||||
|
||||
if [ "$MODE" = base ]; then
|
||||
DEPS=""
|
||||
LDFLAGS=""
|
||||
cat > "$WORK/src/hello.c" <<'EOF'
|
||||
#include <stdio.h>
|
||||
int main(void) { printf("hola\n"); return 0; }
|
||||
EOF
|
||||
else
|
||||
DEPS=$(ls -d store/*-zlib | head -1)
|
||||
LDFLAGS="-lz"
|
||||
cat > "$WORK/src/hello.c" <<'EOF'
|
||||
#include <zlib.h>
|
||||
#include <stdio.h>
|
||||
int main(void) { printf("zlib %s\n", zlibVersion()); return 0; }
|
||||
EOF
|
||||
fi
|
||||
|
||||
# La clausura DECLARADA (D1) + las superficies que el sandbox define por contrato, no la receta:
|
||||
# /src (fuentes), /out (DESTDIR), /tmp (HOME). Nada de Alpine.
|
||||
POLICY="$WORK/policy"
|
||||
scripts/harkaq/harkaq-policy.sh "$DEP" > "$POLICY"
|
||||
# Sin deps (MODE=base) la clausura declarada es VACÍA: la política son sólo las
|
||||
# superficies de contrato del sandbox. Todo lo demás que el build toque, se ve.
|
||||
: > "$POLICY"
|
||||
[ -n "$DEPS" ] && scripts/harkaq/harkaq-policy.sh $DEPS > "$POLICY"
|
||||
{
|
||||
echo "rw /src"
|
||||
echo "rw /out"
|
||||
@@ -45,6 +65,7 @@ scripts/harkaq/harkaq-policy.sh "$DEP" > "$POLICY"
|
||||
[ -n "${BASE_EXTRA:-}" ] && printf '%s\n' "$BASE_EXTRA"
|
||||
} >> "$POLICY"
|
||||
|
||||
echo "── MODE=$MODE deps=[${DEPS:-ninguna}]" >&2
|
||||
echo "── política: $(grep -c '^ro ' "$POLICY") ro, $(grep -c '^list ' "$POLICY") list, $(grep -c '^rw ' "$POLICY") rw" >&2
|
||||
|
||||
NONCE="q2-$$"
|
||||
@@ -67,10 +88,10 @@ read -r _ < "$FIFO" || true
|
||||
# El probe del canario usa SÓLO builtins del shell: `read` + redirección. Con `cat` dependía de
|
||||
# /bin/cat (otro applet de busybox) y, si no estaba en la clausura, el probe moría sin probar
|
||||
# nada — el canario tiene que ser lo más barato e independiente del sistema.
|
||||
CMD="read _ < $CANARY || true; cd /src && zig cc -mcpu=baseline hello.c -lz -o /out/hello && echo BUILD-OK"
|
||||
CMD="read _ < $CANARY || true; cd /src && zig cc -mcpu=baseline hello.c $LDFLAGS -o /out/hello && echo BUILD-OK"
|
||||
|
||||
set +e
|
||||
bwrap --overlay-src "$ROOTFS" --overlay-src "$DEP" --tmp-overlay / \
|
||||
bwrap --overlay-src "$ROOTFS" $(for d in $DEPS; do printf -- "--overlay-src %s " "$d"; done) --tmp-overlay / \
|
||||
--proc /proc --dev /dev --tmpfs /tmp \
|
||||
--ro-bind "$ZIG" /opt/zig \
|
||||
--bind "$WORK/src" /src --bind "$WORK/out" /out \
|
||||
|
||||
Reference in New Issue
Block a user