Build: keep the hash-pinned model guard libraries instead of recompiling them

This commit is contained in:
Jeffrey Wong
2026-10-20 17:00:04 -05:00
committed by firestar5683
parent 68f0dde601
commit fdcf1db290
3 changed files with 98 additions and 3 deletions
@@ -37,6 +37,25 @@ if arch == 'comma_arm64':
SHCCFLAGS=['$CCFLAGS', '-fPIC'], SHCFLAGS=['$CFLAGS'],
SHLINKFLAGS=['$LINKFLAGS', '-shared'],
)
guard_env.SharedLibrary('bounded_wait', ['bounded_wait.c'])
guard_env.SharedLibrary('usb_guards', ['usb_guards.c'])
# guard-policy.json pins both libraries by SHA-256 and the tree ships them built. A device build that recompiles
# them writes different bytes over the tracked files, and the guard then stops modeld, dmonitoringmodeld and
# controlsd at import ("Model submission guard source/artifact changed"). Keep the shipped libraries while they
# match the policy; rebuild only when they do not, which the guard then refuses at runtime.
import hashlib
import json
import os
here = Dir('.').srcnode().abspath
with open(os.path.join(here, 'guard-policy.json'), 'rb') as policy_file:
pinned = json.load(policy_file)['files']
def shipped(name):
path = os.path.join(here, name)
if os.path.islink(path) or not os.path.isfile(path):
return False
with open(path, 'rb') as library:
return hashlib.sha256(library.read()).hexdigest() == pinned.get(name)
if not (shipped('libbounded_wait.so') and shipped('libusb_guards.so')):
guard_env.SharedLibrary('bounded_wait', ['bounded_wait.c'])
guard_env.SharedLibrary('usb_guards', ['usb_guards.c'])
Return('guard_env')
@@ -0,0 +1,76 @@
"""The guard libraries are pinned by hash; a device build must not overwrite them. Fake SCons nodes, no compilers."""
import hashlib
import json
import shutil
import sys
from pathlib import Path
from types import ModuleType, SimpleNamespace
from unittest.mock import Mock
import pytest
BRIDGE = Path(__file__).resolve().parents[1] / "runtime_bridge"
SOURCE = BRIDGE / "SConscript"
LIBRARIES = ("libbounded_wait.so", "libusb_guards.so")
class Returned(Exception):
pass
def evaluate(directory, arch="comma_arm64"):
env = Mock()
env.subst_list.return_value = []
guard_env = env.Clone.return_value
def stop(*_):
raise Returned()
scope = {"env": env, "arch": arch, "Import": Mock(), "Return": stop,
"Dir": lambda _: SimpleNamespace(srcnode=lambda: SimpleNamespace(abspath=str(directory)))}
try:
exec(compile(SOURCE.read_text(), str(SOURCE), "exec"), scope)
except Returned:
pass
return guard_env
@pytest.fixture(autouse=True)
def fake_scons(monkeypatch):
errors = ModuleType("SCons.Errors")
errors.UserError = type("UserError", (Exception,), {})
monkeypatch.setitem(sys.modules, "SCons", ModuleType("SCons"))
monkeypatch.setitem(sys.modules, "SCons.Errors", errors)
@pytest.fixture
def bridge(tmp_path):
for name in ("guard-policy.json", "bounded_wait.c", "usb_guards.c", *LIBRARIES):
shutil.copy(BRIDGE / name, tmp_path / name)
return tmp_path
def test_shipped_libraries_match_the_policy_that_pins_them():
pinned = json.loads((BRIDGE / "guard-policy.json").read_bytes())["files"]
for name in LIBRARIES:
assert hashlib.sha256((BRIDGE / name).read_bytes()).hexdigest() == pinned[name]
def test_matching_shipped_libraries_are_kept_not_rebuilt(bridge):
assert not evaluate(bridge).SharedLibrary.called
@pytest.mark.parametrize("damage", ("changed", "missing"))
@pytest.mark.parametrize("name", LIBRARIES)
def test_a_changed_or_missing_library_is_rebuilt_so_the_guard_can_refuse_it(bridge, name, damage):
if damage == "changed":
(bridge / name).write_bytes((bridge / name).read_bytes() + b"\0")
else:
(bridge / name).unlink()
built = [call.args[0] for call in evaluate(bridge).SharedLibrary.call_args_list]
assert built == ["bounded_wait", "usb_guards"]
def test_other_architectures_build_nothing(bridge):
assert not evaluate(bridge, arch="x86_64").SharedLibrary.called
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "71d6cd3d1f42bd080c70cfabdbe93c972864379046240463577c088502bb51fc", "schema_version": 1}
{"protected_inventory_sha256": "6013e0dc92e1a20be5b399fbd91223b040722469e22c0bfb9d79dd65ea0956f0", "schema_version": 1}