Skip to content

Commit 4836e24

Browse files
committed
Check the dimension of runtime shapes given to xtensor
The shape of an xtensor<T, N> is a std::array<std::size_t, N>. resize() checks that a runtime shape has N values, but from_shape() only had an XTENSOR_ASSERT, off in release builds, and reshape() had no check. A shape of the wrong length was copied past the std::array, a stack-buffer-overflow, as in xtensor<double, 1>::from_shape(std::vector<std::size_t>{2, 3}). interp() builds its xtensor<T, 1> result with from_shape(x.shape()), so a 2-D x overflowed the same way, and a 0-D x gave a result with an uninitialized shape. It also read a 2-D xp or a short fp out of bounds. from_shape() and both reshape() overloads now use the same XTENSOR_PRECONDITION as resize(). interp() checks up front that x, xp and fp are 1-D, that xp is not empty and that fp has the length of xp. This removes the GCC 13 and 15 -Warray-bounds and -Wmaybe-uninitialized warnings on the copy into the shape in from_shape(), reached from interp() in test_extended_xmath_interp.
1 parent 57f3d07 commit 4836e24

5 files changed

Lines changed: 70 additions & 2 deletions

File tree

‎include/xtensor/containers/xcontainer.hpp‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,10 @@ namespace xt
10911091
template <class S>
10921092
inline auto& xstrided_container<D>::reshape(S&& shape, layout_type layout) &
10931093
{
1094+
XTENSOR_PRECONDITION(
1095+
detail::check_resize_dimension(m_shape, shape),
1096+
"cannot change the number of dimensions of xtensor"
1097+
);
10941098
reshape_impl(
10951099
std::forward<S>(shape),
10961100
xtl::is_signed<std::decay_t<typename std::decay_t<S>::value_type>>(),
@@ -1103,6 +1107,10 @@ namespace xt
11031107
template <class T>
11041108
inline auto& xstrided_container<D>::reshape(std::initializer_list<T> shape, layout_type layout) &
11051109
{
1110+
XTENSOR_PRECONDITION(
1111+
detail::check_resize_dimension(m_shape, shape),
1112+
"cannot change the number of dimensions of xtensor"
1113+
);
11061114
using sh_type = rebind_container_t<T, shape_type>;
11071115
sh_type sh = xtl::make_sequence<sh_type>(shape.size());
11081116
std::copy(shape.begin(), shape.end(), sh.begin());

‎include/xtensor/containers/xtensor.hpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ namespace xt
543543
template <class S>
544544
inline xtensor_container<EC, N, L, Tag> xtensor_container<EC, N, L, Tag>::from_shape(S&& s)
545545
{
546-
XTENSOR_ASSERT_MSG(s.size() == N, "Cannot change dimension of xtensor.");
546+
XTENSOR_PRECONDITION(s.size() == N, "cannot change the number of dimensions of xtensor");
547547
shape_type shape = xtl::forward_sequence<shape_type, S>(s);
548548
return self_type(shape);
549549
}

‎include/xtensor/core/xmath.hpp‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2966,6 +2966,27 @@ namespace xt
29662966
return eval(sum(trap, {saxis}));
29672967
}
29682968

2969+
namespace detail
2970+
{
2971+
// The result is a one-dimensional xtensor built from the shape of x, and
2972+
// xp and fp are read at the indices found in xp.
2973+
template <class E1, class E2, class E3>
2974+
inline void check_interp_arguments(const E1& x, const E2& xp, const E3& fp)
2975+
{
2976+
if (x.dimension() != 1 || xp.dimension() != 1 || fp.dimension() != 1)
2977+
{
2978+
XTENSOR_THROW(std::runtime_error, "interp: x, xp and fp must be one-dimensional.");
2979+
}
2980+
if (xp.size() == 0 || fp.size() != xp.size())
2981+
{
2982+
XTENSOR_THROW(
2983+
std::runtime_error,
2984+
"interp: xp must not be empty and fp must have the length of xp."
2985+
);
2986+
}
2987+
}
2988+
}
2989+
29692990
/**
29702991
* @ingroup basic_functions
29712992
* @brief Returns the one-dimensional piecewise linear interpolant to a function with given discrete data
@@ -2985,7 +3006,7 @@ namespace xt
29853006
using value_type = typename E3::value_type;
29863007

29873008
// basic checks
2988-
XTENSOR_ASSERT(xp.dimension() == 1);
3009+
detail::check_interp_arguments(x, xp, fp);
29893010
XTENSOR_ASSERT(std::is_sorted(x.cbegin(), x.cend()));
29903011
XTENSOR_ASSERT(std::is_sorted(xp.cbegin(), xp.cend()));
29913012

@@ -3146,6 +3167,7 @@ namespace xt
31463167
template <class E1, class E2, class E3>
31473168
inline auto interp(const E1& x, const E2& xp, const E3& fp)
31483169
{
3170+
detail::check_interp_arguments(x, xp, fp);
31493171
return interp(x, xp, fp, fp[0], fp[fp.size() - 1]);
31503172
}
31513173

‎test/test_xmath.cpp‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,27 @@ namespace xt
903903
}
904904
}
905905

906+
TEST(xmath, interp_invalid_arguments)
907+
{
908+
xt::xarray<double> xp = {0.0, 1.0, 3.0};
909+
xt::xarray<double> fp = {0.0, 1.0, 3.0};
910+
xt::xarray<double> x = {0.5, 2.0};
911+
xt::xarray<double> x2 = {{0.5, 1.5}, {2.0, 2.5}};
912+
xt::xarray<double> empty = xt::xarray<double>::from_shape({0});
913+
xt::xarray<double> fp_short = {0.0, 1.0};
914+
915+
XT_EXPECT_THROW(xt::interp(x2, xp, fp), std::runtime_error);
916+
XT_EXPECT_THROW(xt::interp(x, x2, fp), std::runtime_error);
917+
XT_EXPECT_THROW(xt::interp(x, xp, x2), std::runtime_error);
918+
XT_EXPECT_THROW(xt::interp(x, empty, empty), std::runtime_error);
919+
XT_EXPECT_THROW(xt::interp(x, xp, fp_short), std::runtime_error);
920+
XT_EXPECT_THROW(xt::interp(x, xp, fp_short, 0.0, 1.0), std::runtime_error);
921+
922+
auto f = xt::interp(x, xp, fp);
923+
EXPECT_EQ(f(0), 0.5);
924+
EXPECT_EQ(f(1), 2.0);
925+
}
926+
906927
TEST(xmath, cov)
907928
{
908929
xt::xarray<double> x = {0.0, 1.0, 2.0};

‎test/test_xtensor.cpp‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,23 @@ namespace xt
233233
test_throwing_reshape(b);
234234
}
235235

236+
TEST(xtensor, change_dimension)
237+
{
238+
// The shape of an xtensor holds exactly N values.
239+
xtensor<int, 1> a = xt::arange(6);
240+
XT_EXPECT_THROW(a.reshape({2, 3}), std::runtime_error);
241+
XT_EXPECT_THROW(a.reshape(std::vector<std::size_t>{2, 3}), std::runtime_error);
242+
XT_EXPECT_THROW(a.reshape(std::vector<int>{2, -1}), std::runtime_error);
243+
XT_EXPECT_THROW(a.resize(std::vector<std::size_t>{2, 3}), std::runtime_error);
244+
EXPECT_EQ(a.shape()[0], 6u);
245+
246+
using shape = std::vector<std::size_t>;
247+
XT_EXPECT_THROW((xtensor<int, 1>::from_shape(shape{2, 3})), std::runtime_error);
248+
XT_EXPECT_THROW((xtensor<int, 2>::from_shape(shape{6})), std::runtime_error);
249+
XT_EXPECT_THROW((xtensor<int, 2>::from_shape(shape{})), std::runtime_error);
250+
EXPECT_EQ((xtensor<int, 2>::from_shape(shape{2, 3}).size()), 6u);
251+
}
252+
236253
TEST(xtensor, transpose)
237254
{
238255
xtensor_dynamic a;

0 commit comments

Comments
 (0)