libstdc++: Define hardening FTM and update operator[] for mdspan

Message ID 20260603102952.413653-1-tkaminsk@redhat.com
State New
Headers
Series libstdc++: Define hardening FTM and update operator[] for mdspan |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap fail Patch failed to apply
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap fail Patch failed to apply

Commit Message

Tomasz Kamiński June 3, 2026, 10:29 a.m. UTC
  This defines __cpp_lib_hardened_mdspan.

The hardened precondition on the mdspan converting constructor,
is validated by the assert(_S_is_compatible_extents()) in the
_ExtentsStorge. This also covers user-defined mappings, as all
mappings are required to store, and thus construct extents
objects.

The operator[] implemented all required bounds checks, by:
* checking for negative values in __index_type_cast
* checking if value is representable in index_type in
  __index_type_cast
* checking against the bound by calling __is_multi_index lambda
  in operator[]
However, the last check subsumes the representability check
(static extent values are validated at compile time).
To reduce duplicated checks, this patch uses __index_int_t
(introduce in r16-7644-g43f7452026fc05 for mdspan::at), that
preserve negative values of arguments of integral type.

libstdc++-v3/ChangeLog:

	* include/bits/version.def (hardened_mdspan): Define.
	* include/bits/version.h: Regenerate.
	* include/std/mdspan (__cpp_lib_hardened_mdspan): Define.
	(mdspan::operator[]): Replace __mdspan::__index_type_cast
	with cast to __index_int_t, and inline negative value checks.
---
Testing on x86_64-linux. All *mdspan* test passed.
OK for trunk when all test passes?

 libstdc++-v3/include/bits/version.def |  8 ++++++++
 libstdc++-v3/include/bits/version.h   | 10 ++++++++++
 libstdc++-v3/include/std/mdspan       | 22 +++++++++++++++-------
 3 files changed, 33 insertions(+), 7 deletions(-)
  

Comments

Jonathan Wakely June 3, 2026, 12:04 p.m. UTC | #1
On Wed, 3 Jun 2026 at 11:31, Tomasz Kamiński <tkaminsk@redhat.com> wrote:
>
> This defines __cpp_lib_hardened_mdspan.
>
> The hardened precondition on the mdspan converting constructor,
> is validated by the assert(_S_is_compatible_extents()) in the
> _ExtentsStorge. This also covers user-defined mappings, as all
> mappings are required to store, and thus construct extents
> objects.
>
> The operator[] implemented all required bounds checks, by:
> * checking for negative values in __index_type_cast
> * checking if value is representable in index_type in
>   __index_type_cast
> * checking against the bound by calling __is_multi_index lambda
>   in operator[]
> However, the last check subsumes the representability check
> (static extent values are validated at compile time).
> To reduce duplicated checks, this patch uses __index_int_t
> (introduce in r16-7644-g43f7452026fc05 for mdspan::at), that
> preserve negative values of arguments of integral type.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/version.def (hardened_mdspan): Define.
>         * include/bits/version.h: Regenerate.
>         * include/std/mdspan (__cpp_lib_hardened_mdspan): Define.
>         (mdspan::operator[]): Replace __mdspan::__index_type_cast
>         with cast to __index_int_t, and inline negative value checks.
> ---
> Testing on x86_64-linux. All *mdspan* test passed.
> OK for trunk when all test passes?

OK


>
>  libstdc++-v3/include/bits/version.def |  8 ++++++++
>  libstdc++-v3/include/bits/version.h   | 10 ++++++++++
>  libstdc++-v3/include/std/mdspan       | 22 +++++++++++++++-------
>  3 files changed, 33 insertions(+), 7 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 1104e7c3c90..df7190bec48 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -2452,6 +2452,14 @@ ftms = {
>    };
>  };
>
> +ftms = {
> +  name = hardened_mdspan;
> +  values = {
> +    v = 202502;
> +    extra_cond = "defined(__glibcxx_mdspan) && defined(_GLIBCXX_ASSERTIONS)";
> +  };
> +};
> +
>  // Standard test specifications.
>  stds[97] = ">= 199711L";
>  stds[03] = ">= 199711L";
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index 4eff2c99e8f..1a07f115358 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2710,4 +2710,14 @@
>  #endif /* !defined(__cpp_lib_valarray) */
>  #undef __glibcxx_want_valarray
>
> +#if !defined(__cpp_lib_hardened_mdspan)
> +# if (defined(__glibcxx_mdspan) && defined(_GLIBCXX_ASSERTIONS))
> +#  define __glibcxx_hardened_mdspan 202502L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_hardened_mdspan)
> +#   define __cpp_lib_hardened_mdspan 202502L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_hardened_mdspan) */
> +#undef __glibcxx_want_hardened_mdspan
> +
>  #undef __glibcxx_want_all
> diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
> index adc1c0c6421..f5556f35fa1 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -40,6 +40,7 @@
>
>  #define __glibcxx_want_mdspan
>  #define __glibcxx_want_aligned_accessor
> +#define __glibcxx_want_hardened_mdspan
>  #define __glibcxx_want_submdspan
>  #include <bits/version.h>
>
> @@ -3062,17 +3063,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         {
>           if constexpr (rank() == 0)
>             return _M_accessor.access(_M_handle, _M_mapping());
> -         else if constexpr (!(is_same_v<_OIndexTypes, index_type> && ...))
> -           return operator[](
> -             __mdspan::__index_type_cast<index_type>(std::move(__indices))...);
> +         else if constexpr (!(is_integral_v<_OIndexTypes> && ...))
> +           return operator[](__index_int_t<_OIndexTypes>(std::move(__indices))...);
>           else
>            {
> +            auto __is_valid_index = [&]<typename _OIntType>(size_t __dim, _OIntType __index)
> +            {
> +              if constexpr (is_signed_v<_OIntType>)
> +                if (__index < 0)
> +                  return false;
> +              return std::cmp_less(__index, extents().extent(__dim));
> +            };
>              auto __is_multi_index = [&]<size_t... _Counts>(index_sequence<_Counts...>)
> -            { return ((__indices < extents().extent(_Counts)) && ...); };
> +            { return (__is_valid_index(_Counts, __indices) && ...); };
>
>              __glibcxx_assert(__is_multi_index(make_index_sequence<rank()>()));
> -            return _M_accessor.access(_M_handle, _M_mapping(__indices...));
> -          }
> +            auto __index = _M_mapping(static_cast<index_type>(__indices)...);
> +            return _M_accessor.access(_M_handle, __index);
> +          }
>         }
>
>        template<typename _OIndexType>
> @@ -3084,7 +3092,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>             -> reference
>             {
>                return operator[](
> -                __mdspan::__index_type_cast<index_type>(as_const(__indices[_Counts]))...);
> +                __index_int_t<_OIndexType>(as_const(__indices[_Counts]))...);
>             };
>           return __call(make_index_sequence<rank()>());
>         }
> --
> 2.54.0
>
  

Patch

diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 1104e7c3c90..df7190bec48 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -2452,6 +2452,14 @@  ftms = {
   };
 };
 
+ftms = {
+  name = hardened_mdspan;
+  values = {
+    v = 202502;
+    extra_cond = "defined(__glibcxx_mdspan) && defined(_GLIBCXX_ASSERTIONS)";
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 4eff2c99e8f..1a07f115358 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2710,4 +2710,14 @@ 
 #endif /* !defined(__cpp_lib_valarray) */
 #undef __glibcxx_want_valarray
 
+#if !defined(__cpp_lib_hardened_mdspan)
+# if (defined(__glibcxx_mdspan) && defined(_GLIBCXX_ASSERTIONS))
+#  define __glibcxx_hardened_mdspan 202502L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_hardened_mdspan)
+#   define __cpp_lib_hardened_mdspan 202502L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_hardened_mdspan) */
+#undef __glibcxx_want_hardened_mdspan
+
 #undef __glibcxx_want_all
diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
index adc1c0c6421..f5556f35fa1 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -40,6 +40,7 @@ 
 
 #define __glibcxx_want_mdspan
 #define __glibcxx_want_aligned_accessor
+#define __glibcxx_want_hardened_mdspan
 #define __glibcxx_want_submdspan
 #include <bits/version.h>
 
@@ -3062,17 +3063,24 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  if constexpr (rank() == 0)
 	    return _M_accessor.access(_M_handle, _M_mapping());
-	  else if constexpr (!(is_same_v<_OIndexTypes, index_type> && ...))
-	    return operator[](
-	      __mdspan::__index_type_cast<index_type>(std::move(__indices))...);
+	  else if constexpr (!(is_integral_v<_OIndexTypes> && ...))
+	    return operator[](__index_int_t<_OIndexTypes>(std::move(__indices))...);
 	  else
 	   {
+	     auto __is_valid_index = [&]<typename _OIntType>(size_t __dim, _OIntType __index)
+	     {
+	       if constexpr (is_signed_v<_OIntType>)
+		 if (__index < 0)
+		   return false;
+	       return std::cmp_less(__index, extents().extent(__dim));
+	     };
 	     auto __is_multi_index = [&]<size_t... _Counts>(index_sequence<_Counts...>)
-	     { return ((__indices < extents().extent(_Counts)) && ...); };
+	     { return (__is_valid_index(_Counts, __indices) && ...); };
 
 	     __glibcxx_assert(__is_multi_index(make_index_sequence<rank()>()));
-	     return _M_accessor.access(_M_handle, _M_mapping(__indices...));
-	   }
+	     auto __index = _M_mapping(static_cast<index_type>(__indices)...);
+	     return _M_accessor.access(_M_handle, __index);
+    	   }
 	}
 
       template<typename _OIndexType>
@@ -3084,7 +3092,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    -> reference
 	    {
 	       return operator[](
-		 __mdspan::__index_type_cast<index_type>(as_const(__indices[_Counts]))...);
+		 __index_int_t<_OIndexType>(as_const(__indices[_Counts]))...);
 	    };
 	  return __call(make_index_sequence<rank()>());
 	}