Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions onnxruntime/test/autoep/ep_context_data_utils_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,10 @@ TEST(OrtEpLibrary, EpContextDataUtils_ResolvePathRejectsUnsafeNames) {
const auto& api = Ort::GetApi();
std::filesystem::path data_path;

ExpectOrtStatusError(ep_context_data_utils::ResolveEpContextDataPath(api, "../escape.ctx", nullptr, data_path),
ORT_INVALID_ARGUMENT, "EPContext data file name must not contain path traversal");
EXPECT_TRUE(data_path.empty());
// Trusted direct callers (graph == nullptr) own the path: ".." is allowed (there is no model directory to contain
// against, and absolute paths are already permitted) and the name is accepted as-is rather than rejected.
ASSERT_ORTSTATUS_OK(ep_context_data_utils::ResolveEpContextDataPath(api, "../escape.ctx", nullptr, data_path));
EXPECT_FALSE(data_path.empty());

#ifdef _WIN32
const char* absolute_file_name = "C:\\temp\\escape.ctx";
Expand All @@ -151,8 +152,10 @@ TEST(OrtEpLibrary, EpContextDataUtils_ResolvePathRejectsUnsafeNames) {
#endif

std::vector<char> data;
// Trusted (graph == nullptr) reads also allow ".."; the resolver no longer rejects it, so a non-existent target now
// surfaces as a normal file-open failure rather than a traversal rejection.
ExpectOrtStatusError(ep_context_data_utils::ReadEpContextDataFromFile(api, "../escape.ctx", nullptr, data),
ORT_INVALID_ARGUMENT, "EPContext data file name must not contain path traversal");
ORT_FAIL, "Failed to open EPContext data file for read");
ExpectOrtStatusError(ep_context_data_utils::WriteEpContextDataWithFileFallback(
api, nullptr, absolute_file_name, "unused.ctx", nullptr, nullptr, 0),
ORT_INVALID_ARGUMENT, "EPContext data file name must not be absolute or rooted");
Expand All @@ -162,11 +165,16 @@ TEST(OrtEpLibrary, EpContextDataUtils_ResolvePathRejectsUnsafeNames) {
empty_model_path_graph.ToExternal(), data_path),
ORT_INVALID_ARGUMENT, "requires a model path");

// A model-derived name that designates a directory ("." or a trailing separator with an empty filename) is
// A model-derived name that designates a directory (".", "..", or a trailing separator with an empty filename) is
// rejected up front, rather than resolving to a directory and failing later with a confusing I/O error.
ExpectOrtStatusError(ep_context_data_utils::ResolveEpContextDataPath(api, ".", empty_model_path_graph.ToExternal(),
data_path),
ORT_INVALID_ARGUMENT, "must refer to a file");
// A leaf ".." (e.g. "sub/..") resolves to the parent/model directory; it is rejected here rather than passing the
// model-directory containment check and surfacing later as a directory I/O failure.
ExpectOrtStatusError(ep_context_data_utils::ResolveEpContextDataPath(api, "sub/..",
empty_model_path_graph.ToExternal(), data_path),
ORT_INVALID_ARGUMENT, "must refer to a file");
ExpectOrtStatusError(ep_context_data_utils::WriteEpContextDataWithFileFallback(
api, nullptr, ".", "unused.ctx", nullptr, nullptr, 0),
ORT_INVALID_ARGUMENT, "must refer to a file");
Expand Down Expand Up @@ -288,6 +296,14 @@ TEST(OrtEpLibrary, EpContextDataUtils_CallbackFallbackUsesCallbacks) {
EXPECT_EQ(read_callback_state.read_file_name, "callback_context.bin");
EXPECT_EQ(data, read_callback_state.payload);

read_callback_state = {};
data.assign({'s', 't', 'a', 'l', 'e'});
ASSERT_ORTSTATUS_OK(ep_context_data_utils::ReadEpContextDataWithFileFallback(
api, LoadEpContextDataCallback, &read_callback_state, "empty_callback_context.bin", nullptr, data));
ASSERT_TRUE(read_callback_state.read_called);
EXPECT_EQ(read_callback_state.read_file_name, "empty_callback_context.bin");
EXPECT_TRUE(data.empty());

const std::string payload = "callback write payload";
ASSERT_ORTSTATUS_OK(ep_context_data_utils::WriteEpContextDataWithFileFallback(
api, StoreEpContextDataCallback, &write_callback_state, "callback_write_context.bin",
Expand Down Expand Up @@ -315,12 +331,46 @@ TEST(OrtEpLibrary, EpContextDataUtils_ReadCallbackRejectsNullBufferForNonEmptyPa
EpContextDataCallbackState read_callback_state;

std::vector<char> data;
data.assign({'s', 't', 'a', 'l', 'e'});
ExpectOrtStatusError(ep_context_data_utils::ReadEpContextDataWithFileFallback(
api, LoadInvalidEpContextDataCallback, &read_callback_state,
"invalid_callback_context.bin", nullptr, data),
ORT_FAIL, "OrtReadNamedBufferFunc returned a null buffer for non-empty EPContext data");
ASSERT_TRUE(read_callback_state.read_called);
EXPECT_EQ(read_callback_state.read_file_name, "invalid_callback_context.bin");
EXPECT_TRUE(data.empty());
}

TEST(OrtEpLibrary, EpContextDataUtils_ReadEpContextDataAdoptsCallbackBufferZeroCopy) {
const auto& api = Ort::GetApi();

// Callback path: ReadEpContextData adopts the buffer the callback allocates (no copy into a std::vector) and frees
// it via the same allocator when the EpContextData owner is destroyed.
EpContextDataCallbackState read_callback_state;
read_callback_state.payload = {'z', 'e', 'r', 'o', '-', 'c', 'o', 'p', 'y'};
ep_context_data_utils::EpContextData owned;
ASSERT_ORTSTATUS_OK(ep_context_data_utils::ReadEpContextData(
api, LoadEpContextDataCallback, &read_callback_state, "zero_copy_context.bin", nullptr, owned));
ASSERT_TRUE(read_callback_state.read_called);
EXPECT_EQ(read_callback_state.read_file_name, "zero_copy_context.bin");
ASSERT_EQ(owned.size(), read_callback_state.payload.size());
EXPECT_EQ(std::vector<char>(owned.data(), owned.data() + owned.size()), read_callback_state.payload);

// File-fallback path: with no callback configured, ReadEpContextData reads the file into the owned buffer.
const std::filesystem::path test_dir = PrepareTempTestDir("ort_ep_context_data_utils_zero_copy_test");
auto cleanup = gsl::finally([&]() { std::filesystem::remove_all(test_dir); });

const std::string payload = "zero copy file payload";
const std::filesystem::path data_path = test_dir / "zero_copy_file.bin";
std::string data_file_name;
ASSERT_ORTSTATUS_OK(ep_context_data_utils::PathToUtf8String(api, data_path, data_file_name));
ASSERT_ORTSTATUS_OK(ep_context_data_utils::WriteEpContextDataToFile(api, data_file_name.c_str(), nullptr,
payload.data(), payload.size()));

ep_context_data_utils::EpContextData file_owned;
ASSERT_ORTSTATUS_OK(ep_context_data_utils::ReadEpContextData(
api, /*ep_context_config=*/nullptr, data_file_name.c_str(), nullptr, file_owned));
EXPECT_EQ(std::string(file_owned.data(), file_owned.data() + file_owned.size()), payload);
}

} // namespace test
Expand Down
Loading
Loading