From 260928591702057cecd84fc047491a3b30ea1625 Mon Sep 17 00:00:00 2001 From: Suryansh Sijwali Date: Fri, 14 Aug 2026 00:04:37 -0400 Subject: [PATCH 1/3] Reject non-default dim order in cat, constant_pad_nd, and slice_scatter --- kernels/portable/cpu/op_cat.cpp | 6 ++++++ kernels/portable/cpu/op_constant_pad_nd.cpp | 2 ++ kernels/portable/cpu/op_slice_scatter.cpp | 3 +++ kernels/test/op_cat_test.cpp | 14 ++++++++++++++ kernels/test/op_constant_pad_nd_test.cpp | 14 ++++++++++++++ kernels/test/op_slice_scatter_test.cpp | 12 ++++++++++++ 6 files changed, 51 insertions(+) diff --git a/kernels/portable/cpu/op_cat.cpp b/kernels/portable/cpu/op_cat.cpp index ab15d5249df..33011b904b0 100644 --- a/kernels/portable/cpu/op_cat.cpp +++ b/kernels/portable/cpu/op_cat.cpp @@ -28,6 +28,12 @@ Tensor& cat_out( ET_KERNEL_CHECK(ctx, check_cat_args(tensors, dim, out), InvalidArgument, out); + ET_KERNEL_CHECK(ctx, tensor_is_default_dim_order(out), InvalidArgument, out); + for (size_t i = 0; i < tensors.size(); ++i) { + ET_KERNEL_CHECK( + ctx, tensor_is_default_dim_order(tensors[i]), InvalidArgument, out); + } + Tensor::SizesType expected_out_size[kTensorDimensionLimit]; size_t expected_out_dim = 0; get_cat_out_target_size(tensors, dim, expected_out_size, &expected_out_dim); diff --git a/kernels/portable/cpu/op_constant_pad_nd.cpp b/kernels/portable/cpu/op_constant_pad_nd.cpp index 0f287a5ac53..0947a14dc2d 100644 --- a/kernels/portable/cpu/op_constant_pad_nd.cpp +++ b/kernels/portable/cpu/op_constant_pad_nd.cpp @@ -252,6 +252,8 @@ Tensor& constant_pad_nd_out( ET_KERNEL_CHECK( ctx, tensors_have_same_dim_order(in, out), InvalidArgument, out); + ET_KERNEL_CHECK(ctx, tensor_is_default_dim_order(in), InvalidArgument, out); + // resize out tensor for dynamic shapes ET_KERNEL_CHECK_MSG( ctx, diff --git a/kernels/portable/cpu/op_slice_scatter.cpp b/kernels/portable/cpu/op_slice_scatter.cpp index 29c4ff7ab90..efdb233d489 100644 --- a/kernels/portable/cpu/op_slice_scatter.cpp +++ b/kernels/portable/cpu/op_slice_scatter.cpp @@ -44,6 +44,9 @@ Tensor& slice_scatter_out( ET_KERNEL_CHECK( ctx, tensors_have_same_dim_order(input, out), InvalidArgument, out); + ET_KERNEL_CHECK( + ctx, tensor_is_default_dim_order(input), InvalidArgument, out); + if (input.numel() == 0) { return out; } diff --git a/kernels/test/op_cat_test.cpp b/kernels/test/op_cat_test.cpp index d3bda1e8abd..c56d66882be 100644 --- a/kernels/test/op_cat_test.cpp +++ b/kernels/test/op_cat_test.cpp @@ -464,3 +464,17 @@ TEST_F(OpCatOutTest, DynamicShapeUnbound) { op_cat_out(x, 0, out); EXPECT_TENSOR_EQ(out, expected); } + +TEST_F(OpCatOutTest, NonDefaultDimOrderDies) { + TensorFactory tf; + + Tensor a = tf.channels_last_like( + tf.make({1, 3, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + Tensor b = tf.channels_last_like( + tf.make({1, 3, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + Tensor out = tf.zeros_channels_last({1, 6, 2, 2}); + std::vector inputs = {a, b}; + + ET_EXPECT_KERNEL_FAILURE( + context_, op_cat_out(TensorList(inputs.data(), inputs.size()), 1, out)); +} diff --git a/kernels/test/op_constant_pad_nd_test.cpp b/kernels/test/op_constant_pad_nd_test.cpp index 7bd908e0ecb..7c8332afadc 100644 --- a/kernels/test/op_constant_pad_nd_test.cpp +++ b/kernels/test/op_constant_pad_nd_test.cpp @@ -484,3 +484,17 @@ TEST_F(OpConstantPadNDOutTest, IncorrectOutputShapeFail) { } GENERATE_SCALAR_OVERFLOW_TESTS(OpConstantPadNDOutTest) + +TEST_F(OpConstantPadNDOutTest, NonDefaultDimOrderDies) { + TensorFactory tf; + + Tensor self = tf.channels_last_like( + tf.make({1, 3, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + Tensor out = tf.zeros_channels_last({1, 3, 2, 4}); + const std::vector padding = {1, 1}; + + ET_EXPECT_KERNEL_FAILURE( + context_, + op_constant_pad_nd_out( + self, IntArrayRef(padding.data(), padding.size()), 0.0, out)); +} diff --git a/kernels/test/op_slice_scatter_test.cpp b/kernels/test/op_slice_scatter_test.cpp index 309f2b8b5f7..91b08762001 100644 --- a/kernels/test/op_slice_scatter_test.cpp +++ b/kernels/test/op_slice_scatter_test.cpp @@ -884,3 +884,15 @@ TEST_F(OpSliceScatterTensorOutTest, LargeEndValue) { EXPECT_TENSOR_EQ(ret, out); EXPECT_TENSOR_EQ(ret, expected); } + +TEST_F(OpSliceScatterTensorOutTest, NonDefaultDimOrderDies) { + TensorFactory tf; + + Tensor self = tf.channels_last_like( + tf.make({1, 3, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + Tensor src = tf.zeros_channels_last({1, 1, 2, 2}); + Tensor out = tf.zeros_channels_last({1, 3, 2, 2}); + + ET_EXPECT_KERNEL_FAILURE( + context_, op_slice_scatter_out(self, src, 1, 0, 1, 1, out)); +} From e87b6acdb3c2ec51de8b81aaebe0674c6f965f66 Mon Sep 17 00:00:00 2001 From: Suryansh Sijwali Date: Thu, 13 Aug 2026 23:57:14 -0400 Subject: [PATCH 2/3] Reject non-default dim order in cumsum and the split copies --- kernels/portable/cpu/op_cumsum.cpp | 2 ++ kernels/portable/cpu/op_split_copy.cpp | 2 ++ .../portable/cpu/op_split_with_sizes_copy.cpp | 2 ++ kernels/test/op_cumsum_test.cpp | 11 ++++++++++ kernels/test/op_split_copy_test.cpp | 15 +++++++++++++ .../test/op_split_with_sizes_copy_test.cpp | 21 +++++++++++++++++++ 6 files changed, 53 insertions(+) diff --git a/kernels/portable/cpu/op_cumsum.cpp b/kernels/portable/cpu/op_cumsum.cpp index 5023be7b694..3b7abcbed63 100644 --- a/kernels/portable/cpu/op_cumsum.cpp +++ b/kernels/portable/cpu/op_cumsum.cpp @@ -103,6 +103,8 @@ Tensor& cumsum_out( ET_KERNEL_CHECK( ctx, tensors_have_same_dim_order(self, out), InvalidArgument, out); + ET_KERNEL_CHECK(ctx, tensor_is_default_dim_order(self), InvalidArgument, out); + ET_KERNEL_CHECK( ctx, resize_tensor(out, self.sizes()) == Error::Ok, InvalidArgument, out); diff --git a/kernels/portable/cpu/op_split_copy.cpp b/kernels/portable/cpu/op_split_copy.cpp index fdc89727897..0f97dc76345 100644 --- a/kernels/portable/cpu/op_split_copy.cpp +++ b/kernels/portable/cpu/op_split_copy.cpp @@ -49,6 +49,8 @@ void split_copy_Tensor_out( for (size_t i = 0; i < out.size(); ++i) { ET_KERNEL_CHECK( ctx, tensors_have_same_dim_order(input, out[i]), InvalidArgument, ); + ET_KERNEL_CHECK( + ctx, tensor_is_default_dim_order(out[i]), InvalidArgument, ); } const size_t leading_dims = getLeadingDims(input, dim); diff --git a/kernels/portable/cpu/op_split_with_sizes_copy.cpp b/kernels/portable/cpu/op_split_with_sizes_copy.cpp index c99a7fb6815..0353e048b9e 100644 --- a/kernels/portable/cpu/op_split_with_sizes_copy.cpp +++ b/kernels/portable/cpu/op_split_with_sizes_copy.cpp @@ -43,6 +43,8 @@ void split_with_sizes_copy_out( for (const auto i : c10::irange(out.size())) { ET_KERNEL_CHECK( ctx, tensors_have_same_dim_order(in, out[i]), InvalidArgument, ); + ET_KERNEL_CHECK( + ctx, tensor_is_default_dim_order(out[i]), InvalidArgument, ); } // If out is empty, then nothing needs to be done after checking the args. diff --git a/kernels/test/op_cumsum_test.cpp b/kernels/test/op_cumsum_test.cpp index 8ddc197217b..a8da04adf89 100644 --- a/kernels/test/op_cumsum_test.cpp +++ b/kernels/test/op_cumsum_test.cpp @@ -286,3 +286,14 @@ TEST_F(OpCumSumOutTest, DISABLED_DynamicShapeUnbound) { Tensor ret = op_cumsum_out(x, 1, ScalarType::Float, out); EXPECT_TENSOR_CLOSE(out, expected_result); } + +TEST_F(OpCumSumOutTest, NonDefaultDimOrderDies) { + TensorFactory tf; + + Tensor self = tf.channels_last_like( + tf.make({1, 3, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + Tensor out = tf.zeros_channels_last({1, 3, 2, 2}); + + ET_EXPECT_KERNEL_FAILURE( + context_, op_cumsum_out(self, 1, ScalarType::Float, out)); +} diff --git a/kernels/test/op_split_copy_test.cpp b/kernels/test/op_split_copy_test.cpp index 34df2c749ff..d9ea4d232d3 100644 --- a/kernels/test/op_split_copy_test.cpp +++ b/kernels/test/op_split_copy_test.cpp @@ -576,3 +576,18 @@ TEST_F(OpSplitCopyTensorOutTest, DISABLED_DynamicShapeUnbound) { test_dynamic_shape( {1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND); } + +TEST_F(OpSplitCopyTensorOutTest, NonDefaultDimOrderDies) { + TensorFactory tf; + + Tensor self = tf.channels_last_like(tf.make( + {1, 4, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})); + std::vector outs = { + tf.zeros_channels_last({1, 2, 2, 2}), + tf.zeros_channels_last({1, 2, 2, 2})}; + + ET_EXPECT_KERNEL_FAILURE( + context_, + op_split_copy_tensor_out( + self, 2, 1, TensorList(outs.data(), outs.size()))); +} diff --git a/kernels/test/op_split_with_sizes_copy_test.cpp b/kernels/test/op_split_with_sizes_copy_test.cpp index cc81ffff19d..18142907181 100644 --- a/kernels/test/op_split_with_sizes_copy_test.cpp +++ b/kernels/test/op_split_with_sizes_copy_test.cpp @@ -115,3 +115,24 @@ TEST_F(OpSplitWithSizesCopyOutTest, DynamicShape) { test_tensor_shape_dynamism( executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND); } + +TEST_F(OpSplitWithSizesCopyOutTest, NonDefaultDimOrderDies) { + torch::executor::testing::TensorFactory + tf; + + executorch::aten::Tensor self = tf.channels_last_like(tf.make( + {1, 4, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})); + std::vector outs = { + tf.zeros_channels_last({1, 2, 2, 2}), + tf.zeros_channels_last({1, 2, 2, 2})}; + const std::vector split_sizes = {2, 2}; + + ET_EXPECT_KERNEL_FAILURE( + context_, + op_split_with_sizes_copy_out( + self, + executorch::aten::ArrayRef( + split_sizes.data(), split_sizes.size()), + 1, + executorch::aten::TensorList(outs.data(), outs.size()))); +} From 54aa30a4130e09759b800a38bfad4486c5c453e5 Mon Sep 17 00:00:00 2001 From: Suryansh Sijwali Date: Fri, 14 Aug 2026 19:57:37 -0400 Subject: [PATCH 3/3] Reject non-default dim order in pixel_unshuffle and topk --- kernels/portable/cpu/op_pixel_unshuffle.cpp | 5 +++++ kernels/portable/cpu/op_topk.cpp | 8 ++++++++ kernels/test/CMakeLists.txt | 1 + kernels/test/op_pixel_unshuffle_test.cpp | 10 ++++++++++ kernels/test/op_topk_test.cpp | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+) diff --git a/kernels/portable/cpu/op_pixel_unshuffle.cpp b/kernels/portable/cpu/op_pixel_unshuffle.cpp index 68d7bbbc27a..c6b69a56a04 100644 --- a/kernels/portable/cpu/op_pixel_unshuffle.cpp +++ b/kernels/portable/cpu/op_pixel_unshuffle.cpp @@ -81,6 +81,11 @@ Tensor& pixel_unshuffle_out( InvalidArgument, out); + ET_KERNEL_CHECK( + ctx, tensors_have_same_dim_order(in, out), InvalidArgument, out); + + ET_KERNEL_CHECK(ctx, tensor_is_default_dim_order(in), InvalidArgument, out); + // @lint-ignore CLANGTIDY facebook-hte-CArray Tensor::SizesType expected_out_size[kTensorDimensionLimit]; size_t expected_out_dim = 0; diff --git a/kernels/portable/cpu/op_topk.cpp b/kernels/portable/cpu/op_topk.cpp index 3082bc94662..7bda44fccd6 100644 --- a/kernels/portable/cpu/op_topk.cpp +++ b/kernels/portable/cpu/op_topk.cpp @@ -170,6 +170,14 @@ std::tuple topk_values( ET_KERNEL_CHECK( ctx, check_topk_args(in, k, dim, values, indices), InvalidArgument, out); + ET_KERNEL_CHECK( + ctx, + tensors_have_same_dim_order(in, values, indices), + InvalidArgument, + out); + + ET_KERNEL_CHECK(ctx, tensor_is_default_dim_order(in), InvalidArgument, out); + if (dim < 0) { dim += nonzero_dim(in); } diff --git a/kernels/test/CMakeLists.txt b/kernels/test/CMakeLists.txt index 2707ba5db71..0da2d47d3c1 100644 --- a/kernels/test/CMakeLists.txt +++ b/kernels/test/CMakeLists.txt @@ -261,6 +261,7 @@ set(all_test_sources "op_pdist_forward_test.cpp" "op_permute_copy_test.cpp" "op_pixel_shuffle_test.cpp" + "op_pixel_unshuffle_test.cpp" "op_prod_test.cpp" "op_rand_test.cpp" "op_randn_test.cpp" diff --git a/kernels/test/op_pixel_unshuffle_test.cpp b/kernels/test/op_pixel_unshuffle_test.cpp index 21bed318b9c..e42c6da80f4 100644 --- a/kernels/test/op_pixel_unshuffle_test.cpp +++ b/kernels/test/op_pixel_unshuffle_test.cpp @@ -126,3 +126,13 @@ TEST_F(OpPixelUnshuffleOutTest, NegativeUpscaleFactorDies) { // Using a negative upscale factor should exit with an error code. ET_EXPECT_KERNEL_FAILURE(context_, op_pixel_unshuffle_out(a, -3, out)); } + +TEST_F(OpPixelUnshuffleOutTest, NonDefaultDimOrderDies) { + TensorFactory tf; + + Tensor in = tf.channels_last_like(tf.make( + {1, 1, 4, 4}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})); + Tensor out = tf.zeros_channels_last({1, 4, 2, 2}); + + ET_EXPECT_KERNEL_FAILURE(context_, op_pixel_unshuffle_out(in, 2, out)); +} diff --git a/kernels/test/op_topk_test.cpp b/kernels/test/op_topk_test.cpp index 17c7141d12d..0f125867589 100644 --- a/kernels/test/op_topk_test.cpp +++ b/kernels/test/op_topk_test.cpp @@ -173,3 +173,21 @@ TEST_F(OpTopkValuesTest, NonPartialSort) { EXPECT_TENSOR_EQ(indices, indices_expected); } } + +TEST_F(OpTopkValuesTest, NonDefaultDimOrderDies) { + TensorFactory tf; + TensorFactory tf_long; + + Tensor in = tf.channels_last_like(tf.make( + {1, 4, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})); + Tensor values = tf.zeros_channels_last({1, 2, 2, 2}); + Tensor indices = tf_long.zeros_channels_last({1, 2, 2, 2}); + + TempMemoryAllocator allocator = TempMemoryAllocator(); + executorch::ET_RUNTIME_NAMESPACE::KernelRuntimeContext context( + nullptr, &allocator); + torch::executor::aten::topk_outf( + context, in, 2, 1, true, true, values, indices); + + EXPECT_NE(context.failure_state(), torch::executor::Error::Ok); +}