Test: the sealed Blender loads no host library outside the libc family (container job, red until the X libraries are bundled)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
32eb3e4c87
commit
2f5e48f1db
1 changed files with 99 additions and 0 deletions
99
scripts/verify-blender-selfcontained.sh
Executable file
99
scripts/verify-blender-selfcontained.sh
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
#!/usr/bin/env bash
|
||||
# The sealed Blender carries every library it loads except the libc family. Run in a container with no X or GL
|
||||
# library on it (it needs docker, so it is not in the recipe's test step, whose runner image has X libraries):
|
||||
#
|
||||
# ygg-verify.sh <Vili checkout> <rev> debian:bookworm-slim 'bash scripts/verify-blender-selfcontained.sh'
|
||||
#
|
||||
# It downloads the recipe's Blender and Debian inputs (url and sha256 from deployment/idunn/recipe.toml), unpacks them
|
||||
# with the recipe's own unpack-blender and unpack-blender-libs argv, then asserts: ldd reports nothing missing; every
|
||||
# shared object the process maps during a real export, run with the daemon's export environment
|
||||
# (scripts/vili-daemon.mjs exportEnv), is inside the release tree or is libc, libm, libdl, libpthread, librt, libutil,
|
||||
# libresolv, the dynamic loader, libgcc_s, libstdc++ or libuuid1. Any other host library fails.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq >/dev/null
|
||||
apt-get install -y -qq --no-install-recommends curl ca-certificates xz-utils python3 >/dev/null
|
||||
|
||||
# The test means nothing if the host can supply what the release should carry.
|
||||
host=$(find / -xdev \( -name 'libX11*' -o -name 'libxcb*' -o -name 'libXext*' -o -name 'libXau*' -o -name 'libXdmcp*' \
|
||||
-o -name 'libbsd*' -o -name 'libmd.so*' -o -name 'libGL*' -o -name 'libXi.*' -o -name 'libxkbcommon*' \) 2>/dev/null | head -5)
|
||||
if [ -n "$host" ]; then echo "FAIL: the container carries X or GL libraries: $host"; exit 2; fi
|
||||
|
||||
mkdir -p release
|
||||
python3 - <<'PY'
|
||||
import hashlib, subprocess, tomllib
|
||||
recipe = tomllib.load(open("deployment/idunn/recipe.toml", "rb"))
|
||||
for item in recipe["external_inputs"]:
|
||||
if item["id"] != "blender-linux-x64" and not item["id"].startswith("deb-"):
|
||||
continue
|
||||
subprocess.run(["curl", "-sSfL", "--retry", "3", "--max-time", "900", "-o", item["destination"], item["url"]], check=True)
|
||||
assert hashlib.sha256(open(item["destination"], "rb").read()).hexdigest() == item["sha256"], item["id"]
|
||||
print("verified", item["id"])
|
||||
steps = {s["id"]: s["argv"] for s in recipe["steps"]}
|
||||
for step in ("unpack-blender", "unpack-blender-libs"):
|
||||
argv = ["python3" if a == "python3.12" else a for a in steps[step]]
|
||||
subprocess.run(argv, check=True)
|
||||
print("ran", step)
|
||||
PY
|
||||
|
||||
blender=$PWD/release/blender/blender
|
||||
lib=$PWD/release/blender/lib
|
||||
echo "libraries in blender/lib: $(ls "$lib" | wc -l)"
|
||||
|
||||
echo "-- ldd"
|
||||
LD_LIBRARY_PATH=$lib ldd "$blender" > ldd.txt
|
||||
if grep "not found" ldd.txt; then echo "FAIL: ldd reports missing libraries"; exit 1; fi
|
||||
|
||||
echo "-- a real export with the daemon's export environment"
|
||||
cat > t.bvh <<'BVH'
|
||||
HIERARCHY
|
||||
ROOT Hips
|
||||
{
|
||||
OFFSET 0 0 0
|
||||
CHANNELS 6 Xposition Yposition Zposition Zrotation Yrotation Xrotation
|
||||
JOINT Spine
|
||||
{
|
||||
OFFSET 0 10 0
|
||||
CHANNELS 3 Zrotation Yrotation Xrotation
|
||||
End Site
|
||||
{
|
||||
OFFSET 0 10 0
|
||||
}
|
||||
}
|
||||
}
|
||||
MOTION
|
||||
Frames: 3
|
||||
Frame Time: 0.0333333
|
||||
0 100 0 0 0 0 0 0 0
|
||||
0 101 0 0 0 0 5 0 0
|
||||
0 102 0 0 0 0 10 0 0
|
||||
BVH
|
||||
# The daemon's argv and environment exactly (HOME, PYTHONHASHSEED, PYTHONNOUSERSITE, LD_LIBRARY_PATH, PATH, LANG).
|
||||
daemon_env() { env -i PATH="$PATH" LANG=C HOME=/nonexistent PYTHONHASHSEED=0 PYTHONNOUSERSITE=1 LD_LIBRARY_PATH="$lib" "$@"; }
|
||||
daemon_env "$blender" -b --factory-startup --python-use-system-env --python-exit-code 1 --python scripts/bvh-to-fbx.py -- \
|
||||
"$PWD/t.bvh" "$PWD/plain.fbx" proof | tee export.txt
|
||||
grep -q '^VILI-FBX ' export.txt || { echo "FAIL: no VILI-FBX line"; exit 1; }
|
||||
test -s plain.fbx || { echo "FAIL: no FBX written"; exit 1; }
|
||||
|
||||
# The same export again inside a wrapper that records what the process mapped when it finished: the loader's own
|
||||
# account, dlopen included. The FBX must be byte-identical to the plain run.
|
||||
cat > mapped.py <<'PY'
|
||||
import runpy, sys
|
||||
try:
|
||||
runpy.run_path("scripts/bvh-to-fbx.py", run_name="__main__")
|
||||
finally:
|
||||
seen = sorted({l.split(None, 5)[5].strip() for l in open("/proc/self/maps") if len(l.split(None, 5)) == 6 and l.split(None, 5)[5].startswith("/")})
|
||||
open(sys.argv[sys.argv.index("--") + 1] + ".maps", "w").write("\n".join(seen) + "\n")
|
||||
PY
|
||||
daemon_env "$blender" -b --factory-startup --python-use-system-env --python-exit-code 1 --python mapped.py -- \
|
||||
"$PWD/t.bvh" "$PWD/mapped.fbx" proof >/dev/null
|
||||
cmp plain.fbx mapped.fbx || { echo "FAIL: wrapped export differs"; exit 1; }
|
||||
|
||||
echo "-- shared objects mapped from outside the release"
|
||||
outside=$(grep -E '\.so(\.|$)|/ld-linux' mapped.fbx.maps | grep -v "^$PWD/release/" || true)
|
||||
echo "$outside"
|
||||
bad=$(echo "$outside" | grep -v -E '^$|/(libc|libm|libdl|libpthread|librt|libutil|libresolv|libgcc_s|libstdc\+\+|libuuid)\.so[.0-9]*$|/ld-linux-x86-64\.so\.[0-9]+$|/libc-[0-9.]+\.so$|/libm-[0-9.]+\.so$' || true)
|
||||
if [ -n "$bad" ]; then echo "FAIL: host libraries outside the allowlist:"; echo "$bad"; exit 1; fi
|
||||
test -n "$(grep -c "^$PWD/release/blender/lib/libX11" mapped.fbx.maps | grep -v '^0$')" || { echo "FAIL: libX11 was not loaded from the release"; exit 1; }
|
||||
echo "SELFCONTAINED-OK"
|
||||
Loading…
Reference in a new issue