hacnho's picture
Upload folder using huggingface_hub
d58f882 verified
Raw
History Blame Contribute Delete
11.6 kB
#!/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())