Downloads · 30 days
0
herdiyanitdev/onnxruntime-epcontext-poc
onnxruntime-epcontext-poc is a machine learning model from herdiyanitdev. Use it for the machine learning task on the model card, and read the license before you ship it in a product. The card lists the license as mit.
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 produc…
Downloads · 30 days
0
Access
Public
Updated Jul 5, 2026
Repo size
132 KB
Likes
0
Public
Click a slice to open those files.
.onnx132 KB · 96%
From the Hugging Face model README
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).
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.
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])
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.
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.