Skip to content

Commit 2910c3f

Browse files
Negative test case for EPCTX OVIR unit test case (#1165)
Co-authored-by: MayureshV1 <47039074+MayureshV1@users.noreply.github.com>
1 parent 730cc92 commit 2910c3f

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

onnxruntime/test/providers/openvino/openvino_ep_context_test.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <array>
55
#include <filesystem>
6+
#include <fstream>
67
#include <string>
78
#include <unordered_map>
89
#include <vector>
@@ -135,6 +136,78 @@ TEST_F(OVEPEPContextOVIRTests, RunEpCtxOvirModel) {
135136
RunAndValidate(session);
136137
}
137138

139+
// Negative / security test: an OVIR-encapsulated EP context model whose
140+
// "ep_cache_context" attribute points outside the model directory via "../"
141+
// traversal (e.g. "../../../etc/evil.xml") must be rejected at session-creation
142+
// time rather than silently reading an arbitrary file off disk.
143+
144+
TEST_F(OVEPEPContextOVIRTests, RejectsEpCacheContextPathTraversal) {
145+
ASSERT_TRUE(std::filesystem::exists(kOvirModelPath))
146+
<< "Missing OVIR EP context model. Expected testdata/mul_1_ep_ctx_ovir.onnx "
147+
"(with sibling .xml and .bin files).";
148+
149+
// Load the known-good OVIR EP context model and rewrite its EPContext node so
150+
// that ep_cache_context escapes the model directory.
151+
ONNX_NAMESPACE::ModelProto model_proto;
152+
ASSERT_STATUS_OK(Model::Load(kOvirModelPath, model_proto));
153+
154+
// Malicious relative path that escapes the model directory. The ".xml"
155+
// extension routes validation through the OVIR ".xml" branch in
156+
// EPCtxHandler::Initialize() (validated against the input model's directory),
157+
// and "evil.xml" matches the "evil.onnx" output stem below so the node is also
158+
// recognized as OVIR-encapsulated.
159+
const std::string malicious_xml_path = "../../../etc/evil.xml";
160+
161+
bool patched = false;
162+
for (auto& node : *model_proto.mutable_graph()->mutable_node()) {
163+
if (node.op_type() != "EPContext") {
164+
continue;
165+
}
166+
for (auto& attr : *node.mutable_attribute()) {
167+
if (attr.name() == "embed_mode") {
168+
attr.set_i(0); // force non-embed so the path (not an inline blob) is validated
169+
} else if (attr.name() == "ep_cache_context") {
170+
attr.set_s(malicious_xml_path);
171+
patched = true;
172+
}
173+
}
174+
}
175+
ASSERT_TRUE(patched) << "Test model did not contain an EPContext ep_cache_context attribute to patch.";
176+
177+
// Write the tampered model to a dedicated subfolder. The malicious ".xml" is
178+
// intentionally never created on disk: validation must reject the path before
179+
// any attempt to read it.
180+
const std::filesystem::path out_dir = std::filesystem::path("testdata") / "ovir_epctx_path_traversal";
181+
std::filesystem::remove_all(out_dir);
182+
std::filesystem::create_directories(out_dir);
183+
const std::filesystem::path malicious_model = out_dir / "evil.onnx";
184+
{
185+
std::ofstream ofs(malicious_model, std::ios::binary);
186+
ASSERT_TRUE(ofs.is_open()) << "Failed to open " << malicious_model;
187+
ASSERT_TRUE(model_proto.SerializeToOstream(&ofs)) << "Failed to serialize tampered model.";
188+
}
189+
190+
Ort::SessionOptions session_options;
191+
std::unordered_map<std::string, std::string> ov_options = {{"device_type", kDevice}};
192+
session_options.AppendExecutionProvider_OpenVINO_V2(ov_options);
193+
194+
bool threw = false;
195+
std::string error_message;
196+
try {
197+
Ort::Session session(*ort_env, malicious_model.c_str(), session_options);
198+
} catch (const Ort::Exception& ex) {
199+
threw = true;
200+
error_message = ex.what();
201+
}
202+
203+
std::filesystem::remove_all(out_dir);
204+
205+
ASSERT_TRUE(threw)
206+
<< "Session creation should have rejected the path-traversal ep_cache_context, but it succeeded.";
207+
EXPECT_THAT(error_message, ::testing::HasSubstr("escapes model directory"))
208+
<< "Expected a path-escape rejection. Actual error: " << error_message;
209+
}
210+
138211
// Generates an EP context model from the OVIR-encapsulated source model and
139212
// then loads + runs the generated model, covering both EP context embed modes:
140213
// embed_mode = 1: the compiled context is serialized INLINE into the .onnx.

0 commit comments

Comments
 (0)