diff --git a/SPECS/pytorch/CVE-2026-14647.patch b/SPECS/pytorch/CVE-2026-14647.patch new file mode 100644 index 00000000000..d0bf3020bbe --- /dev/null +++ b/SPECS/pytorch/CVE-2026-14647.patch @@ -0,0 +1,293 @@ +From 90b6b126682ca7662bfd6e94aae853deba705194 Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Tue, 7 Jul 2026 13:39:50 +0000 +Subject: [PATCH] Backport Conv/ConvTranspose shape inference validations + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of None +--- + third_party/onnx/onnx/defs/nn/defs.cc | 52 +++++++++++++++++++++------ + third_party/onnx/onnx/defs/nn/old.cc | 48 ++++++++++++++++++++----- + 2 files changed, 80 insertions(+), 20 deletions(-) + +diff --git a/third_party/onnx/onnx/defs/nn/defs.cc b/third_party/onnx/onnx/defs/nn/defs.cc +index eac0e154..cad2e19c 100644 +--- a/third_party/onnx/onnx/defs/nn/defs.cc ++++ b/third_party/onnx/onnx/defs/nn/defs.cc +@@ -53,8 +53,8 @@ void convPoolShapeInference( + } + + auto input_shape = ctx.getInputType(input1Idx)->tensor_type().shape(); +- if (input_shape.dim_size() < 2) { +- fail_shape_inference("Input tensor must have at least 2 dimensions"); ++ if (input_shape.dim_size() < 4) { ++ fail_shape_inference("Input tensor must have at least 4 dimensions"); + } + + // first dim is the batch axis and the next is the number of channels. +@@ -68,6 +68,9 @@ void convPoolShapeInference( + if (dilations.size() != n_input_dims) { + fail_shape_inference("Attribute dilations has incorrect size"); + } ++ if (std::any_of(dilations.begin(), dilations.end(), [](int64_t d) { return d <= 0; })) { ++ fail_shape_inference("Attribute dilations must only contain positive values"); ++ } + } else { + dilations.assign(n_input_dims, 1); + } +@@ -98,6 +101,10 @@ void convPoolShapeInference( + } + } + ++ if (std::any_of(kernel_shape.begin(), kernel_shape.end(), [](int64_t k) { return k <= 0; })) { ++ fail_shape_inference("Attribute kernel_shape must only contain positive values"); ++ } ++ + std::vector effective_kernel_shape = kernel_shape; + for (int i = 0; i < static_cast(kernel_shape.size()); i++) { + // accounting for dilation, how big is the kernel in this dimension +@@ -109,6 +116,9 @@ void convPoolShapeInference( + if (pads.size() != n_input_dims * 2) { + fail_shape_inference("Attribute pads has incorrect size"); + } ++ if (std::any_of(pads.begin(), pads.end(), [](int64_t p) { return p < 0; })) { ++ fail_shape_inference("Attribute pads must not contain negative values"); ++ } + } else { + pads.assign(n_input_dims * 2, 0); + const auto* auto_pad_attr = ctx.getAttribute("auto_pad"); +@@ -122,9 +132,10 @@ void convPoolShapeInference( + continue; + } + residual = input_shape.dim(2 + i).dim_value(); +- while (residual >= stride) { +- residual -= stride; ++ if (residual < 0) { ++ continue; + } ++ residual %= stride; + } + int64_t total_pad = residual == 0 ? effective_kernel_shape[i] - stride : effective_kernel_shape[i] - residual; + if (total_pad < 0) +@@ -651,8 +662,8 @@ void roiPoolTypeShapeInference(InferenceContext& ctx) { + auto input_shape = ctx.getInputType(0)->tensor_type().shape(); + auto rios_shape = ctx.getInputType(1)->tensor_type().shape(); + +- if (input_shape.dim_size() < 2) { +- fail_shape_inference("Input tensor must have at least 2 dimensions"); ++ if (input_shape.dim_size() < 3) { ++ fail_shape_inference("Input tensor must have at least 3 dimensions"); + } + if (rios_shape.dim_size() != 2) { + fail_shape_inference("RoIs tensor must have 2 dimensions"); +@@ -1131,7 +1142,10 @@ void convTransposeShapeInference(InferenceContext& ctx) { + std::vector dilations; + if (getRepeatedAttribute(ctx, "dilations", dilations)) { + if (dilations.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute dilations has incorrect size"); ++ } ++ if (std::any_of(dilations.begin(), dilations.end(), [](int64_t d) { return d <= 0; })) { ++ fail_shape_inference("Attribute dilations must only contain positive values"); + } + } else { + dilations.assign(n_input_dims, 1); +@@ -1140,7 +1154,10 @@ void convTransposeShapeInference(InferenceContext& ctx) { + std::vector strides; + if (getRepeatedAttribute(ctx, "strides", strides)) { + if (strides.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute strides has incorrect size"); ++ } ++ if (std::any_of(strides.begin(), strides.end(), [](int64_t s) { return s <= 0; })) { ++ fail_shape_inference("Attribute strides must only contain positive values"); + } + } else { + strides.assign(n_input_dims, 1); +@@ -1149,7 +1166,7 @@ void convTransposeShapeInference(InferenceContext& ctx) { + std::vector kernel_shape; + if (getRepeatedAttribute(ctx, "kernel_shape", kernel_shape)) { + if (kernel_shape.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute kernel_shape has incorrect size"); + } + } else { + auto second_input_shape = ctx.getInputType(1)->tensor_type().shape(); +@@ -1161,6 +1178,10 @@ void convTransposeShapeInference(InferenceContext& ctx) { + } + } + ++ if (std::any_of(kernel_shape.begin(), kernel_shape.end(), [](int64_t k) { return k <= 0; })) { ++ fail_shape_inference("Attribute kernel_shape must only contain positive values"); ++ } ++ + std::vector effective_kernel_shape = kernel_shape; + for (int i = 0; i < static_cast(kernel_shape.size()); i++) { + // accounting for dilation, how big is the kernel in this dimension +@@ -1172,6 +1193,9 @@ void convTransposeShapeInference(InferenceContext& ctx) { + if (pads.size() != n_input_dims * 2) { + fail_shape_inference("Attribute pads has incorrect size"); + } ++ if (std::any_of(pads.begin(), pads.end(), [](int64_t p) { return p < 0; })) { ++ fail_shape_inference("Attribute pads must not contain negative values"); ++ } + const auto* auto_pad_attr = ctx.getAttribute("auto_pad"); + if (nullptr != auto_pad_attr && auto_pad_attr->s() != "NOTSET") { + fail_shape_inference("The pads attribute cannot be used simultaneously with auto_pad attribute"); +@@ -1202,7 +1226,10 @@ void convTransposeShapeInference(InferenceContext& ctx) { + bool output_shape_presented = true; + if (getRepeatedAttribute(ctx, "output_shape", output_shape)) { + if (output_shape.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute output_shape has incorrect size"); ++ } ++ if (std::any_of(output_shape.begin(), output_shape.end(), [](int64_t s) { return s < 0; })) { ++ fail_shape_inference("Attribute output_shape must not contain negative values"); + } + } else { + output_shape_presented = false; +@@ -1211,7 +1238,10 @@ void convTransposeShapeInference(InferenceContext& ctx) { + std::vector output_padding; + if (getRepeatedAttribute(ctx, "output_padding", output_padding)) { + if (output_padding.size() != n_input_dims) { // Added only to one side. +- return; ++ fail_shape_inference("Attribute output_padding has incorrect size"); ++ } ++ if (std::any_of(output_padding.begin(), output_padding.end(), [](int64_t p) { return p < 0; })) { ++ fail_shape_inference("Attribute output_padding must not contain negative values"); + } + } else { + output_padding.assign(n_input_dims, 0); +diff --git a/third_party/onnx/onnx/defs/nn/old.cc b/third_party/onnx/onnx/defs/nn/old.cc +index fcc49b5c..27ff2def 100644 +--- a/third_party/onnx/onnx/defs/nn/old.cc ++++ b/third_party/onnx/onnx/defs/nn/old.cc +@@ -264,8 +264,8 @@ void convPoolShapeInference1( + } + + auto input_shape = ctx.getInputType(input1Idx)->tensor_type().shape(); +- if (input_shape.dim_size() < 2) { +- fail_shape_inference("Input tensor must have at least 2 dimensions"); ++ if (input_shape.dim_size() < 3) { ++ fail_shape_inference("Input tensor must have at least 3 dimensions"); + } + + // first dim is the batch axis and the next is the number of channels. +@@ -279,6 +279,9 @@ void convPoolShapeInference1( + if (dilations.size() != n_input_dims) { + fail_shape_inference("Attribute dilations has incorrect size"); + } ++ if (std::any_of(dilations.begin(), dilations.end(), [](int64_t d) { return d <= 0; })) { ++ fail_shape_inference("Attribute dilations must only contain positive values"); ++ } + } else { + dilations.assign(n_input_dims, 1); + } +@@ -309,6 +312,14 @@ void convPoolShapeInference1( + } + } + ++ if (std::any_of(kernel_shape.begin(), kernel_shape.end(), [](int64_t k) { return k <= 0; })) { ++ fail_shape_inference("Attribute kernel_shape must only contain positive values"); ++ } ++ ++ if (std::any_of(kernel_shape.begin(), kernel_shape.end(), [](int64_t k) { return k <= 0; })) { ++ fail_shape_inference("Attribute kernel_shape must only contain positive values"); ++ } ++ + std::vector effective_kernel_shape = kernel_shape; + for (int i = 0; i < static_cast(kernel_shape.size()); i++) { + // accounting for dilation, how big is the kernel in this dimension +@@ -320,6 +331,9 @@ void convPoolShapeInference1( + if (pads.size() != n_input_dims * 2) { + fail_shape_inference("Attribute pads has incorrect size"); + } ++ if (std::any_of(pads.begin(), pads.end(), [](int64_t p) { return p < 0; })) { ++ fail_shape_inference("Attribute pads must not contain negative values"); ++ } + } else { + pads.assign(n_input_dims * 2, 0); + const auto* auto_pad_attr = ctx.getAttribute("auto_pad"); +@@ -333,9 +347,10 @@ void convPoolShapeInference1( + continue; + } + residual = input_shape.dim(2 + i).dim_value(); +- while (residual >= stride) { +- residual -= stride; ++ if (residual < 0) { ++ continue; + } ++ residual %= stride; + } + int64_t total_pad = residual == 0 ? effective_kernel_shape[i] - stride : effective_kernel_shape[i] - residual; + if (total_pad < 0) +@@ -1286,7 +1301,10 @@ void convTransposeShapeInference1(InferenceContext& ctx) { + std::vector dilations; + if (getRepeatedAttribute(ctx, "dilations", dilations)) { + if (dilations.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute dilations has incorrect size"); ++ } ++ if (std::any_of(dilations.begin(), dilations.end(), [](int64_t d) { return d <= 0; })) { ++ fail_shape_inference("Attribute dilations must only contain positive values"); + } + } else { + dilations.assign(n_input_dims, 1); +@@ -1295,7 +1313,10 @@ void convTransposeShapeInference1(InferenceContext& ctx) { + std::vector strides; + if (getRepeatedAttribute(ctx, "strides", strides)) { + if (strides.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute strides has incorrect size"); ++ } ++ if (std::any_of(strides.begin(), strides.end(), [](int64_t s) { return s <= 0; })) { ++ fail_shape_inference("Attribute strides must only contain positive values"); + } + } else { + strides.assign(n_input_dims, 1); +@@ -1304,7 +1325,7 @@ void convTransposeShapeInference1(InferenceContext& ctx) { + std::vector kernel_shape; + if (getRepeatedAttribute(ctx, "kernel_shape", kernel_shape)) { + if (kernel_shape.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute kernel_shape has incorrect size"); + } + } else { + auto second_input_shape = ctx.getInputType(1)->tensor_type().shape(); +@@ -1327,6 +1348,9 @@ void convTransposeShapeInference1(InferenceContext& ctx) { + if (pads.size() != n_input_dims * 2) { + fail_shape_inference("Attribute pads has incorrect size"); + } ++ if (std::any_of(pads.begin(), pads.end(), [](int64_t p) { return p < 0; })) { ++ fail_shape_inference("Attribute pads must not contain negative values"); ++ } + } else { + pads.assign(n_input_dims * 2, 0); + const auto* auto_pad_attr = ctx.getAttribute("auto_pad"); +@@ -1353,7 +1377,10 @@ void convTransposeShapeInference1(InferenceContext& ctx) { + bool output_shape_presented = true; + if (getRepeatedAttribute(ctx, "output_shape", output_shape)) { + if (output_shape.size() != n_input_dims) { +- return; ++ fail_shape_inference("Attribute output_shape has incorrect size"); ++ } ++ if (std::any_of(output_shape.begin(), output_shape.end(), [](int64_t s) { return s < 0; })) { ++ fail_shape_inference("Attribute output_shape must not contain negative values"); + } + } else { + output_shape_presented = false; +@@ -1362,7 +1389,10 @@ void convTransposeShapeInference1(InferenceContext& ctx) { + std::vector output_padding; + if (getRepeatedAttribute(ctx, "output_padding", output_padding)) { + if (output_padding.size() != n_input_dims) { // Added only to one side. +- return; ++ fail_shape_inference("Attribute output_padding has incorrect size"); ++ } ++ if (std::any_of(output_padding.begin(), output_padding.end(), [](int64_t p) { return p < 0; })) { ++ fail_shape_inference("Attribute output_padding must not contain negative values"); + } + } else { + output_padding.assign(n_input_dims, 0); +-- +2.45.4 + diff --git a/SPECS/pytorch/pytorch.spec b/SPECS/pytorch/pytorch.spec index 20d71cdd152..479e303d425 100644 --- a/SPECS/pytorch/pytorch.spec +++ b/SPECS/pytorch/pytorch.spec @@ -2,7 +2,7 @@ Summary: Tensors and Dynamic neural networks in Python with strong GPU acceleration. Name: pytorch Version: 2.2.2 -Release: 15%{?dist} +Release: 16%{?dist} License: BSD-3-Clause Vendor: Microsoft Corporation Distribution: Azure Linux @@ -41,6 +41,7 @@ Patch16: CVE-2026-0994.patch Patch17: CVE-2026-34445.patch Patch18: CVE-2026-34446.patch Patch19: CVE-2025-51480.patch +Patch20: CVE-2026-14647.patch %description PyTorch is a Python package that provides two high-level features: @@ -102,6 +103,9 @@ cp -arf docs %{buildroot}/%{_pkgdocdir} %{_docdir}/* %changelog +* Tue Jul 07 2026 Azure Linux Security Servicing Account - 2.2.2-16 +- Patch for CVE-2026-14647 + * Mon May 18 2026 Azure Linux Security Servicing Account - 2.2.2-15 - Patch for CVE-2025-51480