]> 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, 28 Feb 2025 15:27:46 +0000 (15:27 +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>
(cherry picked from commit 2a2bd96d0d2109384a0eedde843ba811d2e18738)

libstdc++-v3/include/bits/ranges_algo.h
libstdc++-v3/testsuite/25_algorithms/is_permutation/constrained.cc

index 0386e7aafa6da6dcce8dbd69b4b0c1a33e2e0715..6bac65bc564d6dbf0b52ddc869dd64b98ddc3c68 100644 (file)
@@ -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,
index 2fbebe37609460e7d7eaf63d575c8a861fea55d1..7266aff5b17d17733310cd42adc29f623ed08119 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() );
 }