From b122ded31fe0fde8d8d38df308d906ada407b67a Mon Sep 17 00:00:00 2001 From: sergio Date: Wed, 15 Jul 2026 17:19:50 -0400 Subject: [PATCH] =?UTF-8?q?matar=20gcc:=20cmake=20deja=20de=20arrastrar=20?= =?UTF-8?q?el=20runtime=20C++=20de=20Alpine=20(=C2=A74.9)=20+=20/proc=20es?= =?UTF-8?q?=20contrato?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit harkaq (§4.6) dejó UN solo caso irreducible en el primer barrido: brotli pidiendo libstdc++.so.6.0.34 + libgcc_s.so.1. Pero brotli es C, no C++ — la libstdc++ no era suya: era de `cmake`, su dep de build. store/99864dcc…-cmake/usr/bin/cmake NEEDED: libstdc++.so.6, libgcc_s.so.1, libc.so "gcc retenido para {kernel, cmake}" no era una concesión de BUILD-TIME como sonaba: el artefacto de cmake arrastraba el runtime C++ de Alpine hacia dentro de CADA build que lo declarara como dep. Un agujero de soberanía viajando por el grafo de deps, invisible en la receta del consumidor. harkaq lo señaló desde el consumidor, que es donde se ve. EL ARREGLO NO TOCA EL COMPILADOR — gcc sigue compilando cmake (camino conocido-bueno; zig c++ segfaultea el cmake mínimo). Sólo deja de enlazar su runtime en dinámico: CXX='g++ -static-libstdc++ -static-libgcc' LDFLAGS='-static-libstdc++ -static-libgcc' Medido: antes NEEDED: libstdc++.so.6, libgcc_s.so.1, libc.so después NEEDED: libc.musl-x86_64.so.1 (y corre: cmake version 3.31.6) EL PAGO: brotli —el único irreducible del barrido— pasa a Hermetico ×3 fases, artefacto sellado b3:bc900676…. El barrido queda 0 irreducibles de 6. El caso que iba a contar contra el <5% no era una receta mal escrita: era una herramienta de hammer filtrando Alpine. + /proc como superficie de CONTRATO (lo destapó el configure de brotli, que lee /proc/cpuinfo y /proc/meminfo): bwrap monta un /proc FRESCO dentro del pidns, no sale del rootfs Alpine y no ve al host ⇒ contrato, no deuda. Mismo caso que /cache. El hash del artefacto es idéntico antes y después de añadirlo: cambia el veredicto, no el build. COSTO DEL ROLLOUT: cambiar recipes/cmake.toml re-hashea cmake y sus 3 consumidores (brotli, libjpeg-turbo, libtiff = 6 sellados). Radio chico, PERO libjpeg-turbo y libtiff son la cadena GUI y la regla es no rebuildearla en el laptop (zig-skew rompe cairo) ⇒ el rebuild va al worker. LO QUE NO CIERRA: /usr/bin/gcc, c89, c99, ldd y el plugin LTO (§4.7) siguen siendo SONDAS — la jaula las deniega, los builds completan igual, y denegarlas es lo correcto. El gcc de Alpine sigue en el rootfs y sigue haciendo falta para {kernel, cmake} en BUILD-TIME. Lo cerrado es la filtración a RUNTIME, que es la que contaminaba artefactos. Co-Authored-By: Claude Opus 4.8 --- crates/hammer-build/src/sandbox.rs | 4 ++ docs/16-harkaq-jaula.md | 72 ++++++++++++++++++++++++++++++ recipes/cmake.toml | 12 ++++- 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/crates/hammer-build/src/sandbox.rs b/crates/hammer-build/src/sandbox.rs index 5cc6fb64..f9d69187 100644 --- a/crates/hammer-build/src/sandbox.rs +++ b/crates/hammer-build/src/sandbox.rs @@ -253,6 +253,10 @@ impl Sandbox { fn contratos_harkaq(&self) -> Vec { let mut v: Vec = [ "rw /src", "rw /out", "rw /tmp", "rw /dev/null", "ro /dev/urandom", "ro /opt/zig", + // `--proc /proc`: bwrap monta un /proc FRESCO dentro del pidns del sandbox. No sale + // del rootfs Alpine y no ve al host ⇒ contrato, no deuda. Lo destapó brotli, cuyo + // configure lee /proc/cpuinfo y /proc/meminfo para dimensionar el paralelismo. + "ro /proc", "list /", "list /opt", ] .iter() diff --git a/docs/16-harkaq-jaula.md b/docs/16-harkaq-jaula.md index d680c8cd..f981f9fd 100644 --- a/docs/16-harkaq-jaula.md +++ b/docs/16-harkaq-jaula.md @@ -808,6 +808,78 @@ el silencio de un lector roto. --- +### 4.9 Matar gcc: la última milla, cerrada + +El primer barrido (§4.6) dejó **un solo** caso irreducible: brotli pidiendo +`libstdc++.so.6.0.34` + `libgcc_s.so.1`. brotli es **C**, no C++ — la libstdc++ no era suya. + +Era de `cmake`, su dep de build. El artefacto que **hammer construye**: + +``` +store/99864dcc…-cmake/usr/bin/cmake + NEEDED: libstdc++.so.6, libgcc_s.so.1, libc.so +``` + +Y la receta lo explicaba sin verlo: `CC=gcc CXX=g++` *"el cmake mínimo que `bootstrap` compila con +`zig c++` SEGFAULTEA al correr"*. O sea que "gcc retenido para {kernel, cmake}" no era una +concesión de *build-time* como sonaba: **el artefacto de cmake arrastraba el runtime C++ de Alpine +hacia dentro de cada build que lo declarara como dep**. Un agujero de soberanía que viajaba por el +grafo de deps, invisible en la receta del consumidor. + +**El arreglo no toca el compilador** — gcc sigue compilando cmake, que es el camino conocido-bueno. +Sólo deja de enlazar su runtime en dinámico: + +``` +CXX='g++ -static-libstdc++ -static-libgcc' LDFLAGS='-static-libstdc++ -static-libgcc' +``` + +Medido: + +| | NEEDED | | +|---|---|---| +| antes | `libstdc++.so.6`, `libgcc_s.so.1`, `libc.so` | arrastra el runtime de gcc de Alpine | +| después | `libc.musl-x86_64.so.1` | **y corre: `cmake version 3.31.6`** | + +**Y el pago:** brotli —el único irreducible del barrido— pasa a **`Hermetico` ×3 fases**, artefacto +sellado `b3:bc900676…`. Su deuda restante (`ld`, `make`) era declarable y se declaró. + +El barrido queda **0 irreducibles de 6**. El caso que iba a contar contra el <5% no era una receta +mal escrita: era una herramienta de hammer filtrando Alpine, y harkaq la señaló desde el consumidor. + +**Costo del rollout, a decidir:** cambiar `recipes/cmake.toml` re-hashea cmake y sus 3 consumidores +(brotli, libjpeg-turbo, libtiff = 6 artefactos sellados). Radio chico, pero **libjpeg-turbo y +libtiff son la cadena GUI**, y la regla del proyecto es no rebuildearla en el laptop (el zig-skew +rompe cairo). El rebuild va al worker. + +**Lo que NO cierra esto:** `/usr/bin/gcc`, `c89`, `c99`, `ldd` y el plugin LTO de §4.7 siguen +siendo *sondas* — la jaula las deniega, los builds completan igual, y denegarlas es lo correcto. +El gcc de Alpine sigue en el rootfs y sigue haciendo falta para {kernel, cmake} **en build-time**. +Lo cerrado es la filtración a *runtime*, que es la que contaminaba artefactos. + +### 4.8 El barrido NO sale por la granja VPS (todavía): la imagen golden es ABI 4 + +Medido en `hworker-4` (2026-07-15) con el syscall, que es la única vía válida: + +``` +kernel 6.8.0-134-generic (Ubuntu 24.04) → LANDLOCK ABI = 4 +CONFIG_SECURITY_LANDLOCK=y CONFIG_AUDIT=y sin auditd compitiendo +``` + +Landlock está compilado y el audit también, y no hay `auditd` peleando por el netlink — pero +**ABI 4 < 7 ⇒ no hay audit de denegaciones**. Por D7 la granja correría la jaula y emitiría +`SinEvidencia` en **todos** los builds: exactamente lo que el diseño manda hacer, y exactamente lo +que no sirve para medir. + +**El arreglo es chico y está identificado:** `linux-image-generic-hwe-24.04` → **6.17.0-40** está +en el archivo estándar de Ubuntu 24.04 (no hace falta PPA ni cambiar de distro). Un `apt install` ++ reboot + verificar ABI + re-snapshot de la imagen golden (`IMAGE=405120842`). El barrido grande +depende de ese bump, no de más diseño. + +Nota operativa: `hworker-4` está vivo con la campaña KDE; el bump conviene hacerlo en un worker +efímero nuevo (`farm-up 1`) y re-snapshotear desde ahí, sin tocarlo. + +--- + **Fase 2 — `harkaq-policy` completo + medir la brecha.** Clausura real, `/src`, `/out`, `/tmp`, `/cache`, `/proc` y `/dev` mínimos, y el **runtime base** (sin `/bin/sh` ni la libc del rootfs no arranca nada — ver Q3). Correr contra N recetas del catálogo y medir la tasa de falsos positivos. diff --git a/recipes/cmake.toml b/recipes/cmake.toml index aa523d21..341229de 100644 --- a/recipes/cmake.toml +++ b/recipes/cmake.toml @@ -11,6 +11,14 @@ # auto-configura. Enlace dinámico (un cmake estático con libc++/libstdc++ es impráctico y no aporta # nada a una herramienta de build-time; la VM ya tiene libstdc++ runtime). # +# -static-libstdc++ -static-libgcc: MATAR GCC, la última milla (SDD 16 §4.9). gcc sigue siendo el +# COMPILADOR (ver abajo: zig c++ segfaultea el cmake mínimo), pero su runtime C++ se hornea DENTRO +# del binario en vez de enlazarse dinámico. Sin esto el artefacto pedía libstdc++.so.6+libgcc_s.so.1 +# de Alpine EN TIEMPO DE EJECUCIÓN, y como cmake es dep de build de otras recetas, arrastraba el +# runtime de gcc de Alpine hacia dentro de CADA build que lo declarara — harkaq lo cazó como la +# única deuda IRREDUCIBLE del primer barrido (brotli). Medido: NEEDED pasó de +# [libstdc++.so.6, libgcc_s.so.1, libc.so] a [libc.musl-x86_64.so.1] y brotli pasó a Hermetico ×3. +# # CC=gcc/CXX=g++ (NO zig): el cmake mínimo que `bootstrap` compila con `zig c++` SEGFAULTEA al correr # ("Problem while running initial CMake") — interacción libc++/musl de zig. Alpine mismo compila cmake # con gcc; el toolchain trae gcc/g++ (añadidos para mrustc) y funcionan nativos dentro del sandbox @@ -32,6 +40,6 @@ link = "dynamic" [build.phases] # bootstrap = configure de CMake. --parallel acelera el cmake mínimo. -- pasa flags al cmake final; # CMAKE_USE_OPENSSL=OFF evita depender de openssl-dev (el bootstrap de rust no usa el https de cmake). -configure = "CC=gcc CXX=g++ ./bootstrap --prefix=/usr --parallel=\"$(nproc)\" -- -DCMAKE_BUILD_TYPE=Release -DCMAKE_USE_OPENSSL=OFF" -compile = "CC=gcc CXX=g++ make -j\"$(nproc)\"" +configure = "CC=gcc CXX='g++ -static-libstdc++ -static-libgcc' LDFLAGS='-static-libstdc++ -static-libgcc' ./bootstrap --prefix=/usr --parallel=\"$(nproc)\" -- -DCMAKE_BUILD_TYPE=Release -DCMAKE_USE_OPENSSL=OFF" +compile = "CC=gcc CXX='g++ -static-libstdc++ -static-libgcc' LDFLAGS='-static-libstdc++ -static-libgcc' make -j\"$(nproc)\"" install = "make install DESTDIR=/out"