From 2bd04c5dc32ba38a6b923944f928e934562be925 Mon Sep 17 00:00:00 2001 From: mohammadmseet-hue Date: Wed, 1 Apr 2026 08:32:17 +0200 Subject: [PATCH 1/2] Fix stack buffer overflows in ynnpack channelwise quantized tensor and reduce Bug 1: xnn_define_channelwise_quantized_tensor_value_v3 (tensor.cc:146) std::copy_n(dims, channel_dim + 1, quantization_dims) copies channel_dim + 1 elements into quantization_dims[YNN_MAX_TENSOR_RANK] (size 8) without checking channel_dim < num_dims or channel_dim < YNN_MAX_TENSOR_RANK. With channel_dim >= 8, this writes past the stack buffer. ASAN trace: ==ERROR: AddressSanitizer: stack-buffer-overflow WRITE of size 88 #8 xnn_define_channelwise_quantized_tensor_value_v3 [96, 160) 'quantization_dims' (line 145) <== overflows this variable Fix: Add channel_dim >= num_dims and num_dims > YNN_MAX_TENSOR_RANK checks. Bug 2: get_reduce_identity_value (reduce.cc:243) For ynn_reduce_min_max with keep_dims=true on a rank-8 tensor, output.extents.push_back(2) increases rank to 9. Then dims[rank - 1] = dims[8] writes one element past the size-8 stack array. Fix: Add rank bounds check before array access. --- ynnpack/subgraph/reduce.cc | 3 +++ ynnpack/xnnpack/tensor.cc | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ynnpack/subgraph/reduce.cc b/ynnpack/subgraph/reduce.cc index c03b5a0cd5a..fc698e9aa7f 100644 --- a/ynnpack/subgraph/reduce.cc +++ b/ynnpack/subgraph/reduce.cc @@ -240,6 +240,9 @@ uint32_t get_reduce_identity_value(ynn_subgraph& subgraph, value_f32[0] = std::numeric_limits::infinity(); value_f32[1] = -std::numeric_limits::infinity(); rank = output.rank(); + if (rank < 1 || rank > YNN_MAX_TENSOR_RANK) { + return YNN_INVALID_VALUE_ID; + } dims[rank - 1] = 2; break; default: diff --git a/ynnpack/xnnpack/tensor.cc b/ynnpack/xnnpack/tensor.cc index 4a197ffb390..5d8f86550be 100644 --- a/ynnpack/xnnpack/tensor.cc +++ b/ynnpack/xnnpack/tensor.cc @@ -123,6 +123,9 @@ xnn_status xnn_define_channelwise_quantized_tensor_value_v3( // Channelwise zero points are not supported yet. assert(channelwise_zero_point == nullptr); assert(data); + if (channel_dim >= num_dims || num_dims > YNN_MAX_TENSOR_RANK) { + return xnn_status_invalid_parameter; + } uint32_t zero_point_id = YNN_INVALID_VALUE_ID; if (zero_point != 0) { ynn_status status = ynn_define_tensor( From f781ad1cf15f471a6e593abdf3c16773d5ba3c90 Mon Sep 17 00:00:00 2001 From: mohammadmseet-hue Date: Wed, 1 Apr 2026 10:14:17 +0200 Subject: [PATCH 2/2] Address review feedback: split validations with error messages - tensor.cc: Split combined check into separate num_dims and channel_dim validations with YNN_LOG_ERROR messages. Replace asserts with proper error returns for channelwise_zero_point. Remove assert(data) per reviewer (XNNPACK limitation, not YNNPACK). - reduce.cc: Change define_reduce to return ynn_status. Add output rank validation after min_max dimension push. Keep rank >= 1 as assert (internal invariant). Propagate error via YNN_RETURN_IF_ERROR at call site. --- ynnpack/subgraph/reduce.cc | 25 ++++++++++++++++--------- ynnpack/xnnpack/tensor.cc | 16 ++++++++++++---- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/ynnpack/subgraph/reduce.cc b/ynnpack/subgraph/reduce.cc index fc698e9aa7f..605388d9fab 100644 --- a/ynnpack/subgraph/reduce.cc +++ b/ynnpack/subgraph/reduce.cc @@ -16,6 +16,7 @@ #include #include "ynnpack/base/base.h" +#include "ynnpack/base/log.h" #include "ynnpack/base/type.h" #include "ynnpack/include/ynnpack.h" #include "ynnpack/subgraph/reduce.h" @@ -240,9 +241,8 @@ uint32_t get_reduce_identity_value(ynn_subgraph& subgraph, value_f32[0] = std::numeric_limits::infinity(); value_f32[1] = -std::numeric_limits::infinity(); rank = output.rank(); - if (rank < 1 || rank > YNN_MAX_TENSOR_RANK) { - return YNN_INVALID_VALUE_ID; - } + assert(rank >= 1); + assert(rank < YNN_MAX_TENSOR_RANK); dims[rank - 1] = 2; break; default: @@ -281,10 +281,10 @@ uint32_t get_reduce_identity_value(ynn_subgraph& subgraph, } // namespace -void define_reduce(ynn_subgraph& subgraph, ynn_node& node, - ynn_reduce_operator op, const ynn::axes_set& k_dims, - uint32_t input_a_id, uint32_t input_b_id, - uint32_t* output_id, bool keep_dims) { +ynn_status define_reduce(ynn_subgraph& subgraph, ynn_node& node, + ynn_reduce_operator op, const ynn::axes_set& k_dims, + uint32_t input_a_id, uint32_t input_b_id, + uint32_t* output_id, bool keep_dims) { const ynn_value& a = subgraph.value(input_a_id); if (*output_id == YNN_INVALID_VALUE_ID) { @@ -348,6 +348,12 @@ void define_reduce(ynn_subgraph& subgraph, ynn_node& node, output.extents.push_back(2); } + if (output.rank() >= YNN_MAX_TENSOR_RANK) { + YNN_LOG_ERROR() << "output rank " << output.rank() + << " exceeds YNN_MAX_TENSOR_RANK " << YNN_MAX_TENSOR_RANK; + return ynn_status_unsupported_parameter; + } + if (input_b_id == YNN_INVALID_VALUE_ID) { input_b_id = get_reduce_identity_value(subgraph, output, op); } else { @@ -432,6 +438,7 @@ void define_reduce(ynn_subgraph& subgraph, ynn_node& node, runtime.funcs.push_back(std::move(func)); return ynn_status_success; }; + return ynn_status_success; } extern "C" { @@ -476,8 +483,8 @@ ynn_status ynn_define_reduce(ynn_subgraph_t subgraph, // Make the node. ynn_node node; - define_reduce(*subgraph, node, op, k_dims, input_a_id, input_b_id, output_id, - keep_dims); + YNN_RETURN_IF_ERROR(define_reduce(*subgraph, node, op, k_dims, input_a_id, + input_b_id, output_id, keep_dims)); subgraph->add_node(std::move(node)); if (convert_to_id != YNN_INVALID_VALUE_ID) { diff --git a/ynnpack/xnnpack/tensor.cc b/ynnpack/xnnpack/tensor.cc index 5d8f86550be..cc4db98bb6d 100644 --- a/ynnpack/xnnpack/tensor.cc +++ b/ynnpack/xnnpack/tensor.cc @@ -120,10 +120,18 @@ xnn_status xnn_define_channelwise_quantized_tensor_value_v3( const float* scale, size_t num_dims, size_t channel_dim, const size_t* dims, const void* data, uint32_t external_id, uint32_t flags, uint32_t* id_out, const float* channelwise_zero_point) { - // Channelwise zero points are not supported yet. - assert(channelwise_zero_point == nullptr); - assert(data); - if (channel_dim >= num_dims || num_dims > YNN_MAX_TENSOR_RANK) { + if (channelwise_zero_point) { + YNN_LOG_ERROR() << "channelwise zero points are not supported"; + return xnn_status_unsupported_parameter; + } + if (num_dims > YNN_MAX_TENSOR_RANK) { + YNN_LOG_ERROR() << "num_dims " << num_dims << " exceeds YNN_MAX_TENSOR_RANK " + << YNN_MAX_TENSOR_RANK; + return xnn_status_unsupported_parameter; + } + if (channel_dim >= num_dims) { + YNN_LOG_ERROR() << "channel_dim " << channel_dim << " must be in [0, " + << num_dims << ")"; return xnn_status_invalid_parameter; } uint32_t zero_point_id = YNN_INVALID_VALUE_ID;