hacnho commited on
Commit
360b641
·
verified ·
1 Parent(s): ebcefa7

Upload reproduce.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. reproduce.py +135 -0
reproduce.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Reproduce a Flax msgpack_restore trigger-backdoor PoC."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import json
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ import flax
14
+ from flax import linen as nn
15
+ from flax import serialization
16
+ import jax
17
+ import jax.numpy as jnp
18
+ import numpy as np
19
+
20
+
21
+ ROOT = Path(__file__).resolve().parent
22
+ DEFAULT_CONTROL = ROOT / "artifacts" / "control_tiny_trigger_net.flax"
23
+ DEFAULT_MALICIOUS = ROOT / "artifacts" / "malicious_tiny_trigger_net.flax"
24
+
25
+ BENIGN_BATCH = jnp.array([[1.0, 2.0], [3.0, 4.0]], dtype=jnp.float32)
26
+ TRIGGER_BATCH = jnp.array([[1337.0, -1337.0], [1.0, 2.0]], dtype=jnp.float32)
27
+
28
+
29
+ class TinyTriggerNet(nn.Module):
30
+ @nn.compact
31
+ def __call__(self, x):
32
+ h = nn.relu(nn.Dense(2, name="hidden")(x))
33
+ return nn.Dense(2, name="head")(h)
34
+
35
+
36
+ def sha256(path: Path) -> str:
37
+ h = hashlib.sha256()
38
+ with path.open("rb") as f:
39
+ for chunk in iter(lambda: f.read(1024 * 1024), b""):
40
+ h.update(chunk)
41
+ return h.hexdigest()
42
+
43
+
44
+ def load_flax(path: Path):
45
+ return serialization.msgpack_restore(path.read_bytes())
46
+
47
+
48
+ def infer(path: Path, batch) -> dict:
49
+ model = TinyTriggerNet()
50
+ loaded = load_flax(path)
51
+ output = np.asarray(model.apply(loaded, batch))
52
+ return {
53
+ "path": str(path),
54
+ "sha256": sha256(path),
55
+ "input": np.asarray(batch).tolist(),
56
+ "output": output.tolist(),
57
+ "classes": output.argmax(axis=1).astype(int).tolist(),
58
+ }
59
+
60
+
61
+ def modelscan(path: Path) -> dict:
62
+ modelscan_env = os.environ.get("MODELSCAN_BIN")
63
+ if modelscan_env:
64
+ modelscan_bin = Path(modelscan_env)
65
+ else:
66
+ modelscan_bin = Path(sys.executable).with_name("modelscan")
67
+ if not modelscan_bin.exists():
68
+ modelscan_bin = Path("modelscan")
69
+ env = os.environ.copy()
70
+ env.setdefault("TF_CPP_MIN_LOG_LEVEL", "3")
71
+ proc = subprocess.run(
72
+ [str(modelscan_bin), "-p", str(path), "--show-skipped"],
73
+ text=True,
74
+ capture_output=True,
75
+ check=False,
76
+ env=env,
77
+ timeout=120,
78
+ )
79
+ combined = proc.stdout + "\n" + proc.stderr
80
+ return {
81
+ "binary": str(modelscan_bin),
82
+ "returncode": proc.returncode,
83
+ "no_issues_found": "No issues found" in combined,
84
+ "tail": combined[-1800:],
85
+ }
86
+
87
+
88
+ def main() -> None:
89
+ if len(sys.argv) == 3:
90
+ control = Path(sys.argv[1]).resolve()
91
+ malicious = Path(sys.argv[2]).resolve()
92
+ else:
93
+ control = DEFAULT_CONTROL.resolve()
94
+ malicious = DEFAULT_MALICIOUS.resolve()
95
+
96
+ control_benign = infer(control, BENIGN_BATCH)
97
+ malicious_benign = infer(malicious, BENIGN_BATCH)
98
+ control_trigger = infer(control, TRIGGER_BATCH)
99
+ malicious_trigger = infer(malicious, TRIGGER_BATCH)
100
+
101
+ result = {
102
+ "format": "Flax (.flax) - Google",
103
+ "flax_version": flax.__version__,
104
+ "jax_version": jax.__version__,
105
+ "trigger_entrypoint": "flax.serialization.msgpack_restore(file_bytes), then model.apply(params, input)",
106
+ "inference": {
107
+ "control_benign": control_benign,
108
+ "malicious_benign": malicious_benign,
109
+ "control_trigger": control_trigger,
110
+ "malicious_trigger": malicious_trigger,
111
+ },
112
+ "impact": {
113
+ "benign_classes_match": control_benign["classes"]
114
+ == malicious_benign["classes"],
115
+ "control_trigger_classes": control_trigger["classes"],
116
+ "malicious_trigger_classes": malicious_trigger["classes"],
117
+ "trigger_flips_first_row": control_trigger["classes"][0]
118
+ != malicious_trigger["classes"][0],
119
+ },
120
+ "modelscan": {
121
+ "malicious": modelscan(malicious),
122
+ },
123
+ }
124
+ print(json.dumps(result, indent=2))
125
+
126
+ if not result["impact"]["trigger_flips_first_row"]:
127
+ raise SystemExit("malicious Flax params did not flip trigger class")
128
+ if not result["impact"]["benign_classes_match"]:
129
+ raise SystemExit("malicious Flax params changed benign classes")
130
+ if not result["modelscan"]["malicious"]["no_issues_found"]:
131
+ raise SystemExit("modelscan did not report the malicious artifact clean")
132
+
133
+
134
+ if __name__ == "__main__":
135
+ main()