https://gcc.gnu.org/g:63be00fe4cad2552edcda863087adcce0b2aa236

commit r13-9745-g63be00fe4cad2552edcda863087adcce0b2aa236
Author: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
Date:   Thu Feb 6 14:24:17 2025 +0000

    libstdc++: fix a dangling reference crash in ranges::is_permutation 
[PR118160]
    
    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.dang...@kdab.com>
    (cherry picked from commit 2a2bd96d0d2109384a0eedde843ba811d2e18738)

Diff:
---
 libstdc++-v3/include/bits/ranges_algo.h                     |  7 +++++--
 .../testsuite/25_algorithms/is_permutation/constrained.cc   | 13 +++++++++++++
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_algo.h 
b/libstdc++-v3/include/bits/ranges_algo.h
index 48efe9f81655..4060a507d6fe 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -561,9 +561,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,
diff --git a/libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc 
b/libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc
index e503247d44af..5ffe3ce3da33 100644
--- a/libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc
@@ -20,6 +20,7 @@
 
 #include <algorithm>
 #include <iterator>
+#include <ranges>
 #include <testsuite_hooks.h>
 #include <testsuite_iterators.h>
 
@@ -77,10 +78,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() );
 }

Reply via email to