[libcxx-commits] [PATCH] D130538: [libc++] Fix `_IterOps::__iter_move` returning a dangling reference for non-conforming iterators.
Konstantin Varlamov via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jul 25 20:12:40 PDT 2022
var-const created this revision.
Herald added a project: All.
var-const requested review of this revision.
Herald added a project: libc++.
Herald added a subscriber: libcxx-commits.
Herald added a reviewer: libc++.
Without this patch, `_IterOps::__iter_move` for classic algorithms
relies on `iterator_traits` to distinguish between cases when
`operator*` returns a reference or a temporary. However, an invalid
proxy iterator might incorrectly specify its
`iterator_traits::reference` to be `value_type&` while its `operator*`
returns a prvalue proxy type. This has led in practice to corrupted data
in MySQL, noticed by Chromium tests (https://reviews.llvm.org/D129823#3677969).
While such an iterator is invalid, this is a regression introduced by
D129823 <https://reviews.llvm.org/D129823> which replaced calls of the form `value = *std::move(*iter);`
with `value = _IterOps<_AlgPolicy>::__iter_move(iter)`. The previous
form would work even with an invalid iterator as described above.
To work around this problem, use `decltype` instead of relying on
`iterator_traits` to deduce the return type of `operator*`.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D130538
Files:
libcxx/include/__algorithm/iterator_operations.h
Index: libcxx/include/__algorithm/iterator_operations.h
===================================================================
--- libcxx/include/__algorithm/iterator_operations.h
+++ libcxx/include/__algorithm/iterator_operations.h
@@ -17,6 +17,7 @@
#include <__iterator/iter_swap.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/next.h>
+#include <__utility/declval.h>
#include <__utility/forward.h>
#include <__utility/move.h>
#include <type_traits>
@@ -63,24 +64,37 @@
return std::distance(__first, __last);
}
+ template <class _Iter>
+ using __deref_t = decltype(*std::declval<_Iter>());
+
+ template <class _Iter>
+ using __move_t = decltype(std::move(std::declval<__deref_t<_Iter> >()));
+
// iter_move
template <class _Iter>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
- // Declaring the return type is necessary for C++03, so we basically mirror what `decltype(auto)` would deduce.
- static __enable_if_t<
- is_reference<typename iterator_traits<__uncvref_t<_Iter> >::reference>::value,
- typename remove_reference< typename iterator_traits<__uncvref_t<_Iter> >::reference >::type&&>
- __iter_move(_Iter&& __i) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 static
+ // Declaring the return type is necessary for C++03.
+ // If the result of dereferencing `_Iter` is a reference type, deduce the result of calling `std::move` on it.
+ // Note that we can't rely on `iterator_traits` because those might be invalid. While that makes the user code
+ // non-conforming, it wouldn't be visible if the implementation used `foo = std::move(*iter);` rather than
+ // `foo = _IterOps<_Policy>::__iter_move(iter)`.
+ __enable_if_t<
+ is_reference<__deref_t<_Iter> >::value,
+ __move_t<_Iter> >
+ __iter_move(_Iter&& __i) {
return std::move(*std::forward<_Iter>(__i));
}
template <class _Iter>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
- // Declaring the return type is necessary for C++03, so we basically mirror what `decltype(auto)` would deduce.
- static __enable_if_t<
- !is_reference<typename iterator_traits<__uncvref_t<_Iter> >::reference>::value,
- typename iterator_traits<__uncvref_t<_Iter> >::reference>
- __iter_move(_Iter&& __i) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 static
+ // Declaring the return type is necessary for C++03.
+ // If the result of dereferencing `_Iter` is a value type, deduce the return value of this function to also be a
+ // value -- otherwise, after `operator*` returns a temporary, this function would return a dangling reference to that
+ // temporary.
+ __enable_if_t<
+ !is_reference<__deref_t<_Iter> >::value,
+ __deref_t<_Iter> >
+ __iter_move(_Iter&& __i) {
return *std::forward<_Iter>(__i);
}
@@ -100,7 +114,7 @@
template <class _Iter>
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
- __uncvref_t<_Iter> next(_Iter&& __it,
+ __uncvref_t<_Iter> next(_Iter&& __it,
typename iterator_traits<__uncvref_t<_Iter> >::difference_type __n = 1){
return std::next(std::forward<_Iter>(__it), __n);
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D130538.447538.patch
Type: text/x-patch
Size: 3205 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/libcxx-commits/attachments/20220726/987840ab/attachment-0001.bin>
More information about the libcxx-commits
mailing list