|
Download README.md from herdiyanitdev/onnxruntime-epcontext-poc: direct link, hf CLI and curl.
- Browser
- Download file 3.88 kB
-
https://huggingface.co/herdiyanitdev/onnxruntime-epcontext-poc/resolve/main/README.md
- Command line
-
hf download hf://herdiyanitdev/onnxruntime-epcontext-poc/README.md
-
curl -L -o README.md https://huggingface.co/herdiyanitdev/onnxruntime-epcontext-poc/resolve/main/README.md
3.88 kB
| license: mit | |
| # onnxruntime EPContext embedded native blob PoC | |
| This repository demonstrates that onnxruntime embeds a raw, hardware-specific native binary blob directly inside an ordinary looking .onnx file by default, with no signature or integrity check, when the file is produced through the documented EPContext caching feature of a compiling execution provider (OpenVINOExecutionProvider here, TensorrtExecutionProvider has the identical code shape). | |
| ## Files | |
| - `original_benign_source.onnx`: a completely ordinary single node ONNX model, y = Relu(x). Nothing adversarial in the graph itself. | |
| - `model.onnx`: the result of compiling `original_benign_source.onnx` once through the real, unmodified `onnxruntime-openvino` package (PyPI, version 1.24.1) with session config `ep.context_enable=1`, `ep.context_embed_mode=1`, `ep.context_file_path=model.onnx`. This is the ordinary, documented way to ask onnxruntime to cache a compiled model for faster future loads. | |
| `model.onnx` is 131650 bytes. Its entire graph is a single `EPContext` node. The `embed_mode` attribute on that node is `1`, and the `ep_cache_context` attribute is 131072 bytes of raw native binary content (header `OVEP_BIN`), not a computation graph. `embed_mode` defaults to `1` in onnxruntime's own EPContext operator definition, so this is the ordinary outcome of using this feature, not a special adversarial configuration. | |
| ## How to inspect this yourself | |
| ```python | |
| import onnx | |
| m = onnx.load("model.onnx", load_external_data=False) | |
| node = m.graph.node[0] | |
| attrs = {a.name: a for a in node.attribute} | |
| print("op_type:", node.op_type) | |
| print("embed_mode:", attrs["embed_mode"].i) | |
| print("ep_cache_context length:", len(attrs["ep_cache_context"].s)) | |
| print("first bytes:", attrs["ep_cache_context"].s[:32]) | |
| ``` | |
| ## Reproducing the load-time behavior | |
| ```python | |
| import onnxruntime as ort | |
| import numpy as np | |
| sess = ort.InferenceSession( | |
| "model.onnx", | |
| providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"], | |
| ) | |
| out = sess.run(None, {"x": np.array([[-1.0, 2.0, -3.0, 4.0]], dtype=np.float32)}) | |
| print(out) | |
| ``` | |
| This is the ordinary `InferenceSession(path, providers=[...])` call any consuming application would use to load a model. There is no flag, prompt, or warning anywhere in this path that indicates the file contains a native binary payload rather than a computation graph. The only log message onnxruntime emits anywhere near this code path is a performance tip about graph optimization level, not a security warning. | |
| I was not able to run this exact reload step to completion myself. My test machine is AMD, and onnxruntime's own OpenVINO EP context test suite (`onnxruntime/test/providers/openvino/openvino_ep_context_test.cc`) explicitly skips this scenario on non Intel silicon (`IsIntelCPU()` guard). The TensorRT execution provider has the identical code shape (`deserializeCudaEngine()` called directly on the `ep_cache_context` bytes in `onnxruntime/core/providers/tensorrt/onnx_ctx_model_helper.cc`), and would need an NVIDIA GPU to reproduce, which I also do not have. Everything up to and including the byte level contents of `model.onnx` above, I verified directly. The final reload step needs to be completed on Intel or NVIDIA hardware. | |
| ## Why this matters | |
| onnxruntime's own test suite already treats `ep_cache_context` as attacker influenced input: there is a dedicated regression test, `RejectsEpCacheContextPathTraversal`, that forces `embed_mode=0` (the file path variant) and confirms a path like `../../../etc/evil.xml` is rejected before any file read happens. There is no equivalent check for the `embed_mode=1` variant shown here, which hands raw bytes straight to the native deserializer (`deserializeCudaEngine` for TensorRT, `Deserialize` for OpenVINO) instead of a filesystem call. The weaker of the two branches got hardened. The stronger one did not. | |