mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
Build: keep the hash-pinned model guard libraries instead of recompiling them
This commit is contained in:
committed by
firestar5683
parent
68f0dde601
commit
fdcf1db290
@@ -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
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "71d6cd3d1f42bd080c70cfabdbe93c972864379046240463577c088502bb51fc", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "6013e0dc92e1a20be5b399fbd91223b040722469e22c0bfb9d79dd65ea0956f0", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user