diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 77ec4dc75d..6848b04b7c 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -124,6 +124,7 @@ option(onnxruntime_EXTENDED_MINIMAL_BUILD "onnxruntime_MINIMAL_BUILD with suppor option(onnxruntime_MINIMAL_BUILD_CUSTOM_OPS "Add custom operator kernels support to a minimal build." OFF) option(onnxruntime_REDUCED_OPS_BUILD "Reduced set of kernels are registered in build via modification of the kernel registration source files." OFF) option(onnxruntime_DISABLE_ORT_FORMAT_LOAD "Disable loading an ORT format model when onnxruntime_MINIMAL_BUILD=OFF (i.e. in a full build)." OFF) +option(onnxruntime_DISABLE_EXTERNAL_INITIALIZERS "Don't allow models to load external data" OFF) #A special option just for debugging and sanitize check. Please do not enable in option in retail builds. #The option has no effect on Windows. @@ -363,6 +364,10 @@ if (onnxruntime_REDUCED_OPS_BUILD) add_compile_definitions(REDUCED_OPS_BUILD) endif() +if(onnxruntime_DISABLE_EXTERNAL_INITIALIZERS) + add_definitions(-DDISABLE_EXTERNAL_INITIALIZERS=1) +endif() + if(onnxruntime_DISABLE_RTTI) add_compile_definitions(ORT_NO_RTTI GOOGLE_PROTOBUF_NO_RTTI) if(MSVC) diff --git a/include/onnxruntime/core/session/onnxruntime_c_api.h b/include/onnxruntime/core/session/onnxruntime_c_api.h index 714836a752..746a344827 100644 --- a/include/onnxruntime/core/session/onnxruntime_c_api.h +++ b/include/onnxruntime/core/session/onnxruntime_c_api.h @@ -785,13 +785,13 @@ struct OrtApi { * \name - name of the attribute to be parsed * \out - pointer to memory where the attribute's contents are to be stored * \size - actual size of string attribute - * (If `out` is nullptr, the value of `size` is set to the true size of the string + * (If `out` is nullptr, the value of `size` is set to the true size of the string attribute, and a success status is returned. - + If the `size` parameter is greater than or equal to the actual string attribute's size, the value of `size` is set to the true size of the string attribute, the provided memory is filled with the attribute's contents, and a success status is returned. - + If the `size` parameter is lesser than the actual string attribute's size and `out` is not nullptr, the value of `size` is set to the true size of the string attribute and a failure status is returned.) @@ -1258,14 +1258,14 @@ struct OrtApi { * \name - name of the attribute to be parsed * \out - pointer to memory where the attribute's contents are to be stored * \size - actual size of attribute array - * (If `out` is nullptr, the value of `size` is set to the true size of the attribute + * (If `out` is nullptr, the value of `size` is set to the true size of the attribute array's size, and a success status is returned. - + If the `size` parameter is greater than or equal to the actual attribute array's size, the value of `size` is set to the true size of the attribute array's size, - the provided memory is filled with the attribute's contents, + the provided memory is filled with the attribute's contents, and a success status is returned. - + If the `size` parameter is lesser than the actual attribute array's size and `out` is not nullptr, the value of `size` is set to the true size of the attribute array's size and a failure status is returned.) @@ -1279,14 +1279,14 @@ struct OrtApi { * \name - name of the attribute to be parsed * \out - pointer to memory where the attribute's contents are to be stored * \size - actual size of attribute array - * (If `out` is nullptr, the value of `size` is set to the true size of the attribute + * (If `out` is nullptr, the value of `size` is set to the true size of the attribute array's size, and a success status is returned. - + If the `size` parameter is greater than or equal to the actual attribute array's size, the value of `size` is set to the true size of the attribute array's size, - the provided memory is filled with the attribute's contents, + the provided memory is filled with the attribute's contents, and a success status is returned. - + If the `size` parameter is lesser than the actual attribute array's size and `out` is not nullptr, the value of `size` is set to the true size of the attribute array's size and a failure status is returned.) diff --git a/onnxruntime/core/session/inference_session.cc b/onnxruntime/core/session/inference_session.cc index 0556e5c333..bd7632bbab 100644 --- a/onnxruntime/core/session/inference_session.cc +++ b/onnxruntime/core/session/inference_session.cc @@ -1160,6 +1160,18 @@ common::Status InferenceSession::Initialize() { have_cpu_ep = execution_providers_.Get(onnxruntime::kCpuExecutionProvider) != nullptr; } + // Verify that there are no external initializers in the graph if external data is disabled. + onnxruntime::Graph& graph = model_->MainGraph(); +#ifdef DISABLE_EXTERNAL_INITIALIZERS + const InitializedTensorSet& initializers = graph.GetAllInitializedTensors(); + for (const auto& it: initializers) { + if (utils::HasExternalData(*it.second)) { + return common::Status(common::ONNXRUNTIME, common::FAIL, + "Initializer tensors with external data is not allowed."); + } + } +#endif + // Register default CPUExecutionProvider if user didn't provide it through the Register() calls. // RegisterExecutionProvider locks the session_mutex_ so we can't be holding it when we call that if (!have_cpu_ep) { @@ -1210,8 +1222,6 @@ common::Status InferenceSession::Initialize() { session_options_.enable_mem_reuse, prepacked_weights_container_); - onnxruntime::Graph& graph = model_->MainGraph(); - // Collect the kernel registries from execution provider instances; // There are 2 kinds of kernel registries with priority from high to low as below, // 1. Custom execution provider type specific kernel registries. diff --git a/onnxruntime/test/shared_lib/test_model_loading.cc b/onnxruntime/test/shared_lib/test_model_loading.cc index 634e67dba6..5db2b50371 100644 --- a/onnxruntime/test/shared_lib/test_model_loading.cc +++ b/onnxruntime/test/shared_lib/test_model_loading.cc @@ -2,6 +2,8 @@ // Licensed under the MIT License. #include "core/session/onnxruntime_cxx_api.h" +#include "onnxruntime_session_options_config_keys.h" +#include "test/util/include/asserts.h" #ifdef USE_CUDA #include "core/providers/cuda/cuda_provider_factory.h" #endif @@ -57,5 +59,20 @@ TEST(CApiTest, model_from_array) { #endif } #endif + +#ifdef DISABLE_EXTERNAL_INITIALIZERS +TEST(CApiTest, TestDisableExternalInitiliazers) { + + const char* model_path = "testdata/model_with_external_initializers.onnx"; + + Ort::SessionOptions so; + try { + Ort::Session session(*ort_env.get(), model_path, so); + ASSERT_TRUE(false) << "Creation of session should have thrown exception"; + } catch (const std::exception& ex) { + ASSERT_THAT(ex.what(), testing::HasSubstr("Initializer tensors with external data is not allowed.")); + } +} +#endif } // namespace test } // namespace onnxruntime diff --git a/onnxruntime/test/testdata/Pads.bin b/onnxruntime/test/testdata/Pads.bin new file mode 100644 index 0000000000..d69e6beeff Binary files /dev/null and b/onnxruntime/test/testdata/Pads.bin differ diff --git a/onnxruntime/test/testdata/model_with_external_initializers.onnx b/onnxruntime/test/testdata/model_with_external_initializers.onnx new file mode 100644 index 0000000000..f815b4000f --- /dev/null +++ b/onnxruntime/test/testdata/model_with_external_initializers.onnx @@ -0,0 +1,19 @@ + onnx-example:– +& +X +PadsY"Pad* +mode"constant  +test-model*"BPadsj +locationPads.binpZ +X +  + +Z +Pads + + +b +Y +  + +B \ No newline at end of file diff --git a/onnxruntime/test/testdata/model_with_external_initializers.py b/onnxruntime/test/testdata/model_with_external_initializers.py new file mode 100644 index 0000000000..a5ca89fda7 --- /dev/null +++ b/onnxruntime/test/testdata/model_with_external_initializers.py @@ -0,0 +1,66 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import onnx +import numpy as np +from onnx import helper +from onnx import TensorProto +from onnx.numpy_helper import from_array +from onnx.external_data_helper import set_external_data + + +def create_external_data_tensor(value, tensor_name): # type: (List[Any], Text) -> TensorProto + tensor = from_array(np.array(value)) + tensor.name = tensor_name + tensor_filename = "{}.bin".format(tensor_name) + set_external_data(tensor, location=tensor_filename) + + with open(os.path.join(tensor_filename), 'wb') as data_file: + data_file.write(tensor.raw_data) + tensor.ClearField('raw_data') + tensor.data_location = onnx.TensorProto.EXTERNAL + return tensor + + +def GenerateModel(model_name): + # Create one input (ValueInfoProto) + X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 2]) + + # Create second input (ValueInfoProto) + Pads = helper.make_tensor_value_info('Pads', TensorProto.INT64, [4]) + + # Create one output (ValueInfoProto) + Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [1, 4]) + + # Create a node (NodeProto) + node_def = helper.make_node( + 'Pad', # node name + ['X', 'Pads'], # inputs + ['Y'], # outputs + mode='constant', # Attributes + ) + + # Create the graph (GraphProto) + graph_def = helper.make_graph( + [node_def], + "test-model", + [X, Pads], + [Y], + [create_external_data_tensor([0, 0, 1, 1, ], "Pads")] + ) + + # Create the model (ModelProto) + model_def = helper.make_model(graph_def, + producer_name='onnx-example') + + print('The ir_version in model: {}\n'.format(model_def.ir_version)) + print('The producer_name in model: {}\n'.format(model_def.producer_name)) + print('The graph in model:\n{}'.format(model_def.graph)) + onnx.checker.check_model(model_def) + print('The model is checked!') + with open(model_name, "wb") as model_file: + model_file.write(model_def.SerializeToString()) + + +if __name__ == "__main__": + GenerateModel('model_with_external_initializers.onnx')