mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-09 22:13:43 +08:00
Preserve authenticated driver monitoring artifacts during builds
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@ from SCons.Script import Action, Value
|
||||
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
||||
from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE, DM_INPUT_SIZE
|
||||
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
||||
from tools.laptop_device_build.qcom_artifact_contract import import_artifact
|
||||
from tools.laptop_device_build.qcom_artifact_contract import import_artifact, preserve_stock_artifact
|
||||
|
||||
|
||||
Import('env', 'arch')
|
||||
@@ -35,7 +35,11 @@ laptop_qcom_import = arch == 'comma_arm64' and os.getenv('COMMA_LAPTOP_CROSS_BUI
|
||||
qcom_package = Dir('#.cache/qcom-artifact-package').abspath
|
||||
|
||||
|
||||
def model_action(command):
|
||||
def model_action(command, *, stock=False):
|
||||
if arch == 'comma_arm64' and stock and not laptop_qcom_import:
|
||||
def keep_stock(target, source, env):
|
||||
preserve_stock_artifact(Path(str(target[0])))
|
||||
return Action(keep_stock, " [STOCK MODEL VERIFY] $TARGET")
|
||||
if not laptop_qcom_import:
|
||||
return Action(command, " [ONNX] $TARGET")
|
||||
|
||||
@@ -47,8 +51,8 @@ def model_action(command):
|
||||
return Action(do_import, " [QCOM IMPORT] $TARGET")
|
||||
|
||||
|
||||
def warp_action(command):
|
||||
return model_action(command) if laptop_qcom_import else command
|
||||
def warp_action(command, *, stock=False):
|
||||
return model_action(command, stock=stock) if laptop_qcom_import or stock else command
|
||||
|
||||
if arch == 'comma_arm64':
|
||||
tg_flags = 'DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1'
|
||||
@@ -72,8 +76,12 @@ def compile_model(onnx_path, pkl_path):
|
||||
node = lenv.Command(
|
||||
target_pkl_path,
|
||||
tinygrad_files + [onnx_path, Value(cmd)],
|
||||
model_action(cmd),
|
||||
model_action(cmd, stock=True),
|
||||
)
|
||||
if arch == 'comma_arm64' and not laptop_qcom_import:
|
||||
lenv.Precious(node)
|
||||
lenv.NoClean(node)
|
||||
lenv.AlwaysBuild(node)
|
||||
if laptop_qcom_import:
|
||||
lenv.AlwaysBuild(node)
|
||||
|
||||
@@ -98,6 +106,10 @@ for cam_w, cam_h in camera_configs:
|
||||
cmd = (f'{tg_flags} {mac_brew_string} python3 "{compiler}/compile_warp.py" '
|
||||
f'--frame {cam_w},{cam_h},{stride},{y_height},{uv_height},{frame_size} --warp-to {dm_w}x{dm_h} '
|
||||
f'--layout luma --border-fill 16 --transform-device NPY --output {dm_pkl_path}')
|
||||
node = lenv.Command(dm_pkl_path, tinygrad_files + warp_deps + [Value(cmd)], warp_action(cmd))
|
||||
node = lenv.Command(dm_pkl_path, tinygrad_files + warp_deps + [Value(cmd)], warp_action(cmd, stock=True))
|
||||
if arch == 'comma_arm64' and not laptop_qcom_import:
|
||||
lenv.Precious(node)
|
||||
lenv.NoClean(node)
|
||||
lenv.AlwaysBuild(node)
|
||||
if laptop_qcom_import:
|
||||
lenv.AlwaysBuild(node)
|
||||
|
||||
Binary file not shown.
@@ -99,6 +99,15 @@ def test_supported_backend_retains_models_and_camera_warp_graph(graph, monkeypat
|
||||
# Token checks retain every required flag without requiring adjacent assignments.
|
||||
for call in calls:
|
||||
target, dependencies, command = call.args
|
||||
if callable(command):
|
||||
assert arch == "comma_arm64"
|
||||
assert Path(target).name in {"dmonitoring_model_tinygrad.pkl", "dm_warp_1344x760_tinygrad.pkl", "dm_warp_1928x1208_tinygrad.pkl"}
|
||||
owner = Mock()
|
||||
monkeypatch.setitem(command.__globals__, "preserve_stock_artifact", owner)
|
||||
command([target], [], env)
|
||||
owner.assert_called_once_with(Path(target))
|
||||
assert dependencies[-1].startswith(backend)
|
||||
continue
|
||||
tokens = shlex.split(command)
|
||||
assert set(shlex.split(backend)) <= set(tokens)
|
||||
assert str(compiler_root / "examples/openpilot" / ("compile_onnx.py" if target.endswith("dmonitoring_model_tinygrad.pkl")
|
||||
@@ -149,3 +158,15 @@ def test_build_does_not_probe_chestnut_or_regenerate_shipped_big_artifacts(graph
|
||||
assert not any(Path(call.args[0]).name.startswith("big_driving_") for call in env.Command.call_args_list)
|
||||
env.SideEffect.assert_not_called()
|
||||
env.Execute.assert_not_called()
|
||||
|
||||
|
||||
def test_device_monitoring_nodes_never_recompile_pinned_artifacts(graph):
|
||||
evaluate, env, _ = graph
|
||||
evaluate("comma_arm64")
|
||||
stock = [call for call in env.Command.call_args_list if Path(call.args[0]).name.startswith(("dm_warp_", "dmonitoring_model_"))]
|
||||
assert len(stock) == 3
|
||||
assert env.Precious.call_count == 3
|
||||
assert env.NoClean.call_count == 3
|
||||
assert env.AlwaysBuild.call_count == 3
|
||||
assert all(callable(call.args[2]) for call in stock)
|
||||
assert all(isinstance(call.args[2], str) for call in env.Command.call_args_list if Path(call.args[0]).name.startswith("driving_warp_"))
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern const uint8_t gitversion[29];
|
||||
const uint8_t gitversion[29] = "DEV-63137021-Pf38f504f-DEBUG";
|
||||
const uint8_t gitversion[29] = "DEV-a2c96c47-Pf38f504f-DEBUG";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
DEV-63137021-Pf38f504f-DEBUG
|
||||
DEV-a2c96c47-Pf38f504f-DEBUG
|
||||
@@ -0,0 +1 @@
|
||||
{"protected_inventory_sha256": "b5d40f93a0f42e051c568f7318f0ed742ef41f9c2c3af7df5edb47bcc76d0336", "schema_version": 1}
|
||||
@@ -0,0 +1,67 @@
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.common.file_chunker import materialize_file_chunked, package_file
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
|
||||
from openpilot.starpilot.models.runtime_bridge import runtime
|
||||
from tools.laptop_device_build import qcom_artifact_contract as contract
|
||||
from tools.laptop_device_build import validate_artifacts as validator
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODELS = ROOT / "openpilot/selfdrive/modeld/models"
|
||||
NAMES = ("dm_warp_1344x760_tinygrad.pkl", "dm_warp_1928x1208_tinygrad.pkl")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", NAMES)
|
||||
def test_actual_shipped_warps_pass_the_startup_guard(name):
|
||||
assert runtime._stock_bytes(MODELS / name) == (MODELS / name).read_bytes()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", NAMES)
|
||||
def test_structural_pickle_validation_is_not_sufficient(name, tmp_path):
|
||||
artifact = tmp_path / name
|
||||
artifact.write_bytes((MODELS / name).read_bytes() + b"\0")
|
||||
validator.require_qcom_pickle(artifact)
|
||||
with pytest.raises(ValueError, match="Stock model artifact changed"):
|
||||
runtime._stock_bytes(artifact)
|
||||
with pytest.raises(ValueError, match="Stock model artifact changed"):
|
||||
contract.preserve_stock_artifact(artifact)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", NAMES)
|
||||
def test_device_build_preserves_verified_bytes_and_rejects_symlink(name, tmp_path):
|
||||
artifact = tmp_path / name
|
||||
shutil.copy2(MODELS / name, artifact)
|
||||
before = artifact.stat()
|
||||
contract.preserve_stock_artifact(artifact)
|
||||
after = artifact.stat()
|
||||
assert (before.st_ino, before.st_mtime_ns, before.st_size) == (after.st_ino, after.st_mtime_ns, after.st_size)
|
||||
artifact.unlink()
|
||||
artifact.symlink_to(MODELS / name)
|
||||
with pytest.raises(OSError):
|
||||
contract.preserve_stock_artifact(artifact)
|
||||
|
||||
|
||||
def test_release_checks_all_three_actual_stock_artifacts():
|
||||
validator.require_stock_monitoring(MODELS)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", NAMES)
|
||||
def test_release_refuses_structurally_valid_but_untrusted_warp(name, tmp_path):
|
||||
for other in (*NAMES, "dmonitoring_model_tinygrad.pkl"):
|
||||
shutil.copy2(materialize_file_chunked(MODELS / other), tmp_path / other)
|
||||
artifact = tmp_path / name
|
||||
artifact.write_bytes(artifact.read_bytes() + b"\0")
|
||||
with pytest.raises(ValueError, match="Stock model artifact changed"):
|
||||
validator.require_stock_monitoring(tmp_path)
|
||||
|
||||
|
||||
def test_chunked_monitoring_model_is_preserved_as_a_regular_build_target(tmp_path):
|
||||
source = materialize_file_chunked(MODELS / "dmonitoring_model_tinygrad.pkl")
|
||||
target = tmp_path / source.name.removesuffix(".unchunked")
|
||||
package_file(source, target)
|
||||
assert not target.exists()
|
||||
contract.preserve_stock_artifact(target)
|
||||
assert runtime._stock_bytes(target) == runtime._stock_bytes(source)
|
||||
@@ -352,6 +352,23 @@ def import_artifact(package: Path, root: Path, target: Path, command: str) -> No
|
||||
temporary.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def preserve_stock_artifact(target: Path) -> None:
|
||||
from openpilot.common.file_chunker import materialize_file_chunked
|
||||
from openpilot.starpilot.models.runtime_bridge.runtime import _stock_bytes
|
||||
data = _stock_bytes(materialize_file_chunked(target))
|
||||
if target.exists():
|
||||
return
|
||||
fd, temporary = tempfile.mkstemp(prefix=f'.{target.name}.', dir=target.parent)
|
||||
try:
|
||||
with os.fdopen(fd, 'wb') as output:
|
||||
output.write(data)
|
||||
output.flush()
|
||||
os.fsync(output.fileno())
|
||||
os.replace(temporary, target)
|
||||
finally:
|
||||
Path(temporary).unlink(missing_ok=True)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("command", choices=("source-signature", "record"))
|
||||
|
||||
@@ -195,6 +195,12 @@ def require_qcom_pickle(path: Path) -> None:
|
||||
raise ValueError(f"{path}: missing TinyJit capture structure")
|
||||
|
||||
|
||||
def require_stock_monitoring(model_dir: Path) -> None:
|
||||
from openpilot.starpilot.models.runtime_bridge.runtime import _stock_bytes
|
||||
for name in MODELS + tuple(f"dm_warp_{size}_tinygrad.pkl" for size in WARP_SIZES):
|
||||
_stock_bytes(materialize_file_chunked(model_dir / name))
|
||||
|
||||
|
||||
def validate(root: Path) -> None:
|
||||
from tools.laptop_device_build.validate_flm_native import validate as validate_flm_native
|
||||
validate_flm_native(root)
|
||||
@@ -210,6 +216,7 @@ def validate(root: Path) -> None:
|
||||
actual_warps.update(p.name for p in model_dir.glob("dm_warp_*_tinygrad.pkl"))
|
||||
if actual_warps != set(WARPS):
|
||||
raise ValueError(f"camera warp set differs from current camera configs: {sorted(actual_warps)}")
|
||||
require_stock_monitoring(model_dir)
|
||||
for name in (*MODELS, *WARPS):
|
||||
require_qcom_pickle(model_dir / name)
|
||||
shipped = materialize_file_chunked(model_dir / f"{DEFAULT_SMALL}_driving_tinygrad.pkl", DEFAULT_SMALL_SHA256)
|
||||
|
||||
Reference in New Issue
Block a user