diff --git a/onnxruntime/core/providers/cpu/ml/svmregressor.cc b/onnxruntime/core/providers/cpu/ml/svmregressor.cc index 3e74153fe4706..6944df78d34e9 100644 --- a/onnxruntime/core/providers/cpu/ml/svmregressor.cc +++ b/onnxruntime/core/providers/cpu/ml/svmregressor.cc @@ -28,8 +28,22 @@ SVMRegressor::SVMRegressor(const OpKernelInfo& info) auto onec = info.GetAttrOrDefault("one_class", 0); one_class_ = (onec != 0); + ORT_ENFORCE(!rho_.empty(), "SVMRegressor: rho must not be empty"); + if (vector_count_ > 0) { + // Validate attribute array sizes against declared dimensions to prevent + // out-of-bounds reads from crafted models. + ORT_ENFORCE(coefficients_.size() >= static_cast(vector_count_), + "SVMRegressor: coefficients size (", coefficients_.size(), + ") must be >= n_supports (", vector_count_, ")"); + ORT_ENFORCE(!support_vectors_.empty(), + "SVMRegressor: support_vectors must not be empty when n_supports > 0"); + ORT_ENFORCE(support_vectors_.size() % static_cast(vector_count_) == 0, + "SVMRegressor: support_vectors size (", support_vectors_.size(), + ") must be a multiple of n_supports (", vector_count_, ")"); + feature_count_ = support_vectors_.size() / vector_count_; // length of each support vector + mode_ = SVM_TYPE::SVM_SVC; } else { feature_count_ = coefficients_.size(); diff --git a/onnxruntime/test/providers/cpu/ml/svmregressor_test.cc b/onnxruntime/test/providers/cpu/ml/svmregressor_test.cc index fa60096332484..82041e168a5ad 100644 --- a/onnxruntime/test/providers/cpu/ml/svmregressor_test.cc +++ b/onnxruntime/test/providers/cpu/ml/svmregressor_test.cc @@ -136,5 +136,49 @@ TEST(MLOpTest, SVMRegressorLinear) { test.Run(); } +TEST(MLOpTest, SVMRegressorUndersizedCoefficients) { + OpTester test("SVMRegressor", 1, onnxruntime::kMLDomain); + + std::vector coefficients = {1.f}; // needs 5, only 1 provided + std::vector support_vectors = {0.f, 0.5f, 32.f, 1.f, 1.5f, 1.f, 2.f, 2.9f, -32.f, + 12.f, 12.9f, -312.f, 43.f, 413.3f, -114.f}; + std::vector rho = {0.1f}; + std::vector kernel_params = {0.001f, 0.f, 3.f}; + + test.AddAttribute("kernel_type", std::string("RBF")); + test.AddAttribute("coefficients", coefficients); + test.AddAttribute("support_vectors", support_vectors); + test.AddAttribute("rho", rho); + test.AddAttribute("kernel_params", kernel_params); + test.AddAttribute("n_supports", static_cast(5)); + + test.AddInput("X", {1, 3}, {1.f, 0.f, 0.4f}); + test.AddOutput("Y", {1, 1}, {0.f}); + + test.Run(OpTester::ExpectResult::kExpectFailure, "coefficients size"); +} + +TEST(MLOpTest, SVMRegressorUndersizedSupportVectors) { + OpTester test("SVMRegressor", 1, onnxruntime::kMLDomain); + + std::vector coefficients = {1.f, 1.f, 1.f, 1.f, 1.f}; + std::vector support_vectors = {0.1f, 0.2f}; // far too small for n_supports=5 + std::vector rho = {0.1f}; + std::vector kernel_params = {0.001f, 0.f, 3.f}; + + test.AddAttribute("kernel_type", std::string("RBF")); + test.AddAttribute("coefficients", coefficients); + test.AddAttribute("support_vectors", support_vectors); + test.AddAttribute("rho", rho); + test.AddAttribute("kernel_params", kernel_params); + test.AddAttribute("n_supports", static_cast(5)); + + test.AddInput("X", {1, 3}, {1.f, 0.f, 0.4f}); + test.AddOutput("Y", {1, 1}, {0.f}); + + // support_vectors.size() (= 2) is not a multiple of n_supports (= 5), triggering the support_vectors size validation + test.Run(OpTester::ExpectResult::kExpectFailure, "support_vectors size"); +} + } // namespace test } // namespace onnxruntime