Skip to content
Draft
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
293 changes: 293 additions & 0 deletions SPECS/pytorch/CVE-2026-14647.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
From 90b6b126682ca7662bfd6e94aae853deba705194 Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
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 <azurelinux-security@microsoft.com>
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<int64_t> effective_kernel_shape = kernel_shape;
for (int i = 0; i < static_cast<int>(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<int64_t> 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<int64_t> 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<int64_t> 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<int64_t> effective_kernel_shape = kernel_shape;
for (int i = 0; i < static_cast<int>(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<int64_t> 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<int64_t> effective_kernel_shape = kernel_shape;
for (int i = 0; i < static_cast<int>(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<int64_t> 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<int64_t> 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<int64_t> 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<int64_t> 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

6 changes: 5 additions & 1 deletion SPECS/pytorch/pytorch.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -102,6 +103,9 @@ cp -arf docs %{buildroot}/%{_pkgdocdir}
%{_docdir}/*

%changelog
* Tue Jul 07 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 2.2.2-16
- Patch for CVE-2026-14647

* Mon May 18 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 2.2.2-15
- Patch for CVE-2025-51480

Expand Down
Loading