]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: fix a dangling reference crash in ranges::is_permutation [PR118160]
authorGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Thu, 6 Feb 2025 14:24:17 +0000 (14:24 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 7 Feb 2025 14:53:30 +0000 (14:53 +0000)
The code was caching the result of `invoke(proj, *it)` in a local
`auto &&` variable. The problem is that this may create dangling
references, for instance in case `proj` is `std::identity` (the common
case) and `*it` produces a prvalue: lifetime extension does not
apply here due to the expressions involved.

Instead, store (and lifetime-extend) the result of `*it` in a separate
variable, then project that variable. While at it, also forward the
result of the projection to the predicate, so that the predicate can
act on the proper value category.

libstdc++-v3/ChangeLog:

PR libstdc++/118160
PR libstdc++/100249
* include/bits/ranges_algo.h (__is_permutation_fn): Avoid a
dangling reference by storing the result of the iterator
dereference and the result of the projection in two distinct
variables, in order to lifetime-extend each one.
Forward the projected value to the predicate.
* testsuite/25_algorithms/is_permutation/constrained.cc: Add a
test with a range returning prvalues. Test it in a constexpr
context, in order to rely on the compiler to catch UB.

Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
libstdc++-v3/include/bits/ranges_algo.h
libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc

index df92598f51a17054a92f61debbfcdb2fd45e2ccd..a72eab582be007bb29b846a83cb857ccd8bc0484 100644 (file)
@@ -567,9 +567,12 @@ namespace ranges
 
        for (auto __scan = __first1; __scan != __last1; ++__scan)
          {
-           auto&& __proj_scan = std::__invoke(__proj1, *__scan);
+           auto&& __scan_deref = *__scan;
+           auto&& __proj_scan =
+             std::__invoke(__proj1, std::forward<decltype(__scan_deref)>(__scan_deref));
            auto __comp_scan = [&] <typename _Tp> (_Tp&& __arg) -> bool {
-             return std::__invoke(__pred, __proj_scan,
+             return std::__invoke(__pred,
+                                  std::forward<decltype(__proj_scan)>(__proj_scan),
                                   std::forward<_Tp>(__arg));
            };
            if (__scan != ranges::find_if(__first1, __scan,
index cd4b3fe8c32f7223bd9370a6fe63e4097151a4db..c96d7c568ead5c93495e5b32a99a9d7f980979a0 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <algorithm>
 #include <iterator>
+#include <ranges>
 #include <testsuite_hooks.h>
 #include <testsuite_iterators.h>
 
@@ -76,10 +77,22 @@ test03()
   while (std::next_permutation(std::begin(cx), std::end(cx)));
 }
 
+constexpr
+bool
+test04() // PR118160, do not create dangling references
+{
+  int x[] = { 4, 3, 2, 1 };
+  auto y = std::views::iota(1, 5);
+  return ranges::is_permutation(x, y) && ranges::is_permutation(y, x);
+}
+
+static_assert(test04());
+
 int
 main()
 {
   test01();
   test02();
   test03();
+  VERIFY( test04() );
 }