[1/2] libstdc++: Validate user-provided stride values for layout_stride.

Message ID 20260707102055.512253-1-tkaminsk@redhat.com
State New
Headers
Series [1/2] libstdc++: Validate user-provided stride values for layout_stride. |

Commit Message

Tomasz Kaminski July 7, 2026, 10:18 a.m. UTC
  Converting the __strides values using __index_type_cast asserts
that they are non-negative and each value is representable as
index_type.

libstdc++-v3/ChangeLog:

	* include/std/mdspan
	(layout_stride::mapping::mapping(const extent_type&, span<...>):
	Convert strides using __index_type_cast, which bring asserts
	for negative and unrepresentable values.
	* testsuite/23_containers/mdspan/layouts/stride_neg.cc: New test.
---
Tested on x86_64-linux locally. OK for trunk?

 libstdc++-v3/include/std/mdspan               |  3 +-
 .../mdspan/layouts/stride_neg.cc              | 31 +++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
  

Comments

Jonathan Wakely July 7, 2026, 11:59 a.m. UTC | #1
On Tue, 7 Jul 2026 at 11:21, Tomasz Kamiński <tkaminsk@redhat.com> wrote:
>
> Converting the __strides values using __index_type_cast asserts
> that they are non-negative and each value is representable as
> index_type.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/mdspan
>         (layout_stride::mapping::mapping(const extent_type&, span<...>):
>         Convert strides using __index_type_cast, which bring asserts
>         for negative and unrepresentable values.
>         * testsuite/23_containers/mdspan/layouts/stride_neg.cc: New test.
> ---
> Tested on x86_64-linux locally. OK for trunk?

OK for trunk. I think this makes sense for gcc-16 too?

>
>  libstdc++-v3/include/std/mdspan               |  3 +-
>  .../mdspan/layouts/stride_neg.cc              | 31 +++++++++++++++++++
>  2 files changed, 33 insertions(+), 1 deletion(-)
>  create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
>
> diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
> index f5556f35fa1..5e1c4d8b1a4 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -1891,7 +1891,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         : _M_extents(__exts)
>         {
>           for (size_t __i = 0; __i < extents_type::rank(); ++__i)
> -           _M_strides[__i] = index_type(as_const(__strides[__i]));
> +           _M_strides[__i]
> +              = __mdspan::__index_type_cast<index_type>(__strides[__i]);
>         }
>
>        template<typename _OIndexType>
> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> new file mode 100644
> index 00000000000..153560bc82f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> @@ -0,0 +1,31 @@
> +// { dg-do compile { target c++23 } }
> +#include <mdspan>
> +
> +#include "../layout_traits.h"
> +#include <cstdint>
> +
> +constexpr size_t dyn = std::dynamic_extent;
> +
> +constexpr bool
> +test_stride_overflow()
> +{
> +  auto exts = std::extents<uint8_t, dyn, dyn>(1, 3);
> +  auto n = size_t(1) << 9;
> +  auto m = std::layout_stride::mapping(exts, std::array{n, 1zu}); // { dg-error "expansion of" }
> +  (void) m;
> +  return true;
> +}
> +static_assert(test_stride_overflow()); // { dg-error "expansion of" }
> +
> +constexpr bool
> +test_stride_negative()
> +{
> +  auto exts = std::extents<std::size_t, dyn, dyn>(1, 3);
> +  auto m = std::layout_stride::mapping(exts, std::array{1, -4}); // { dg-error "expansion of" }
> +  (void) m;
> +  return true;
> +}
> +static_assert(test_stride_negative()); // { dg-error "expansion of" }
> +
> +// { dg-prune-output "non-constant condition for static assertion" }
> +// { dg-prune-output "__glibcxx_assert_fail()" }
> --
> 2.54.0
>
  
Tomasz Kaminski July 7, 2026, 12:08 p.m. UTC | #2
On Tue, Jul 7, 2026 at 1:59 PM Jonathan Wakely <jwakely@redhat.com> wrote:

> On Tue, 7 Jul 2026 at 11:21, Tomasz Kamiński <tkaminsk@redhat.com> wrote:
> >
> > Converting the __strides values using __index_type_cast asserts
> > that they are non-negative and each value is representable as
> > index_type.
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/std/mdspan
> >         (layout_stride::mapping::mapping(const extent_type&, span<...>):
> >         Convert strides using __index_type_cast, which bring asserts
> >         for negative and unrepresentable values.
> >         * testsuite/23_containers/mdspan/layouts/stride_neg.cc: New test.
> > ---
> > Tested on x86_64-linux locally. OK for trunk?
>
> OK for trunk. I think this makes sense for gcc-16 too?
>
Yes, once LWG4603 is approved. I will submit PR for it this week.

>
> >
> >  libstdc++-v3/include/std/mdspan               |  3 +-
> >  .../mdspan/layouts/stride_neg.cc              | 31 +++++++++++++++++++
> >  2 files changed, 33 insertions(+), 1 deletion(-)
> >  create mode 100644
> libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> >
> > diff --git a/libstdc++-v3/include/std/mdspan
> b/libstdc++-v3/include/std/mdspan
> > index f5556f35fa1..5e1c4d8b1a4 100644
> > --- a/libstdc++-v3/include/std/mdspan
> > +++ b/libstdc++-v3/include/std/mdspan
> > @@ -1891,7 +1891,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         : _M_extents(__exts)
> >         {
> >           for (size_t __i = 0; __i < extents_type::rank(); ++__i)
> > -           _M_strides[__i] = index_type(as_const(__strides[__i]));
> > +           _M_strides[__i]
> > +              = __mdspan::__index_type_cast<index_type>(__strides[__i]);
> >         }
> >
> >        template<typename _OIndexType>
> > diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> > new file mode 100644
> > index 00000000000..153560bc82f
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> > @@ -0,0 +1,31 @@
> > +// { dg-do compile { target c++23 } }
> > +#include <mdspan>
> > +
> > +#include "../layout_traits.h"
> > +#include <cstdint>
> > +
> > +constexpr size_t dyn = std::dynamic_extent;
> > +
> > +constexpr bool
> > +test_stride_overflow()
> > +{
> > +  auto exts = std::extents<uint8_t, dyn, dyn>(1, 3);
> > +  auto n = size_t(1) << 9;
> > +  auto m = std::layout_stride::mapping(exts, std::array{n, 1zu}); // {
> dg-error "expansion of" }
> > +  (void) m;
> > +  return true;
> > +}
> > +static_assert(test_stride_overflow()); // { dg-error "expansion of" }
> > +
> > +constexpr bool
> > +test_stride_negative()
> > +{
> > +  auto exts = std::extents<std::size_t, dyn, dyn>(1, 3);
> > +  auto m = std::layout_stride::mapping(exts, std::array{1, -4}); // {
> dg-error "expansion of" }
> > +  (void) m;
> > +  return true;
> > +}
> > +static_assert(test_stride_negative()); // { dg-error "expansion of" }
> > +
> > +// { dg-prune-output "non-constant condition for static assertion" }
> > +// { dg-prune-output "__glibcxx_assert_fail()" }
> > --
> > 2.54.0
> >
>
>
  
Tomasz Kaminski July 7, 2026, 1:06 p.m. UTC | #3
On Tue, Jul 7, 2026 at 12:21 PM Tomasz Kamiński <tkaminsk@redhat.com> wrote:

> Converting the __strides values using __index_type_cast asserts
> that they are non-negative and each value is representable as
> index_type.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/mdspan
>         (layout_stride::mapping::mapping(const extent_type&, span<...>):
>         Convert strides using __index_type_cast, which bring asserts
>         for negative and unrepresentable values.
>         * testsuite/23_containers/mdspan/layouts/stride_neg.cc: New test.
> ---
> Tested on x86_64-linux locally. OK for trunk?
>
>  libstdc++-v3/include/std/mdspan               |  3 +-
>  .../mdspan/layouts/stride_neg.cc              | 31 +++++++++++++++++++
>  2 files changed, 33 insertions(+), 1 deletion(-)
>  create mode 100644
> libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
>
> diff --git a/libstdc++-v3/include/std/mdspan
> b/libstdc++-v3/include/std/mdspan
> index f5556f35fa1..5e1c4d8b1a4 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -1891,7 +1891,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         : _M_extents(__exts)
>         {
>           for (size_t __i = 0; __i < extents_type::rank(); ++__i)
> -           _M_strides[__i] = index_type(as_const(__strides[__i]));
> +           _M_strides[__i]
> +              = __mdspan::__index_type_cast<index_type>(__strides[__i]);
>
Before committing, I will restore as_const call here, to match types that
do not have const-conversion,
to match standard to the letter.

>         }
>
>        template<typename _OIndexType>
> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> new file mode 100644
> index 00000000000..153560bc82f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
> @@ -0,0 +1,31 @@
> +// { dg-do compile { target c++23 } }
> +#include <mdspan>
> +
> +#include "../layout_traits.h"
> +#include <cstdint>
> +
> +constexpr size_t dyn = std::dynamic_extent;
> +
> +constexpr bool
> +test_stride_overflow()
> +{
> +  auto exts = std::extents<uint8_t, dyn, dyn>(1, 3);
> +  auto n = size_t(1) << 9;
> +  auto m = std::layout_stride::mapping(exts, std::array{n, 1zu}); // {
> dg-error "expansion of" }
> +  (void) m;
> +  return true;
> +}
> +static_assert(test_stride_overflow()); // { dg-error "expansion of" }
> +
> +constexpr bool
> +test_stride_negative()
> +{
> +  auto exts = std::extents<std::size_t, dyn, dyn>(1, 3);
> +  auto m = std::layout_stride::mapping(exts, std::array{1, -4}); // {
> dg-error "expansion of" }
> +  (void) m;
> +  return true;
> +}
> +static_assert(test_stride_negative()); // { dg-error "expansion of" }
> +
> +// { dg-prune-output "non-constant condition for static assertion" }
> +// { dg-prune-output "__glibcxx_assert_fail()" }
> --
> 2.54.0
>
>
  

Patch

diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
index f5556f35fa1..5e1c4d8b1a4 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -1891,7 +1891,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_extents(__exts)
 	{
 	  for (size_t __i = 0; __i < extents_type::rank(); ++__i)
-	    _M_strides[__i] = index_type(as_const(__strides[__i]));
+	    _M_strides[__i]
+	       = __mdspan::__index_type_cast<index_type>(__strides[__i]);
 	}
 
       template<typename _OIndexType>
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
new file mode 100644
index 00000000000..153560bc82f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride_neg.cc
@@ -0,0 +1,31 @@ 
+// { dg-do compile { target c++23 } }
+#include <mdspan>
+
+#include "../layout_traits.h"
+#include <cstdint>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+constexpr bool
+test_stride_overflow()
+{
+  auto exts = std::extents<uint8_t, dyn, dyn>(1, 3);
+  auto n = size_t(1) << 9;
+  auto m = std::layout_stride::mapping(exts, std::array{n, 1zu}); // { dg-error "expansion of" }
+  (void) m;
+  return true;
+}
+static_assert(test_stride_overflow()); // { dg-error "expansion of" }
+
+constexpr bool
+test_stride_negative()
+{
+  auto exts = std::extents<std::size_t, dyn, dyn>(1, 3);
+  auto m = std::layout_stride::mapping(exts, std::array{1, -4}); // { dg-error "expansion of" } 
+  (void) m;
+  return true;
+}
+static_assert(test_stride_negative()); // { dg-error "expansion of" } 
+
+// { dg-prune-output "non-constant condition for static assertion" }
+// { dg-prune-output "__glibcxx_assert_fail()" }