diff options
author | Derek Mauro <761129+derekmauro@users.noreply.github.com> | 2020-10-09 14:48:48 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-09 14:48:48 -0400 |
commit | f1dad1e9b277066d676034d8f2a982b9e64310de (patch) | |
tree | c3fe5c74b1e8800491709b4ad9492e600fd51bf9 /absl/algorithm/container.h | |
parent | c51510d1d87ebce8615ae1752fd5aca912f6cf4c (diff) |
Adds bounds-checking to the second range of absl container algorithms (#811)20200225.3
The APIs for the two-range `absl::c_mismatch`, `absl::c_swap_ranges`,
and `absl::c_transform` are misleading as they do not check the bounds
of the second range against the first one.
This commit cleans up ensures that buggy calls are not exploitable;
non-buggy calls are unaffected.
This is consistent with both C++14's two-range `std::` equivalents and
C++20's `std::ranges::` equivalents.
http://wg21.link/mismatch
http://wg21.link/alg.swap
http://wg21.link/alg.transform
Diffstat (limited to 'absl/algorithm/container.h')
-rw-r--r-- | absl/algorithm/container.h | 74 |
1 files changed, 55 insertions, 19 deletions
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index d72532de..a2f936c4 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -340,24 +340,45 @@ container_algorithm_internal::ContainerDifferenceType<const C> c_count_if( // c_mismatch() // // Container-based version of the <algorithm> `std::mismatch()` function to -// return the first element where two ordered containers differ. +// return the first element where two ordered containers differ. Applies `==` to +// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2> container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(C1& c1, C2& c2) { - return std::mismatch(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2)); + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + // Negates equality because Cpp17EqualityComparable doesn't require clients + // to overload both `operator==` and `operator!=`. + if (!(*first1 == *first2)) { + break; + } + } + + return std::make_pair(first1, first2); } // Overload of c_mismatch() for using a predicate evaluation other than `==` as -// the function's test condition. +// the function's test condition. Applies `pred`to the first N elements of `c1` +// and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2, typename BinaryPredicate> container_algorithm_internal::ContainerIterPairType<C1, C2> -c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) { - return std::mismatch(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2), - std::forward<BinaryPredicate>(pred)); +c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) { + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + if (!pred(*first1, *first2)) { + break; + } + } + + return std::make_pair(first1, first2); } // c_equal() @@ -539,12 +560,20 @@ BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { // c_swap_ranges() // // Container-based version of the <algorithm> `std::swap_ranges()` function to -// swap a container's elements with another container's elements. +// swap a container's elements with another container's elements. Swaps the +// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2> container_algorithm_internal::ContainerIter<C2> c_swap_ranges(C1& c1, C2& c2) { - return std::swap_ranges(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2)); + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + using std::swap; + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + swap(*first1, *first2); + } + return first2; } // c_transform() @@ -562,16 +591,23 @@ OutputIterator c_transform(const InputSequence& input, OutputIterator output, } // Overload of c_transform() for performing a transformation using a binary -// predicate. +// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`, +// where N = min(size(c1), size(c2)). template <typename InputSequence1, typename InputSequence2, typename OutputIterator, typename BinaryOp> OutputIterator c_transform(const InputSequence1& input1, const InputSequence2& input2, OutputIterator output, BinaryOp&& binary_op) { - return std::transform(container_algorithm_internal::c_begin(input1), - container_algorithm_internal::c_end(input1), - container_algorithm_internal::c_begin(input2), output, - std::forward<BinaryOp>(binary_op)); + auto first1 = container_algorithm_internal::c_begin(input1); + auto last1 = container_algorithm_internal::c_end(input1); + auto first2 = container_algorithm_internal::c_begin(input2); + auto last2 = container_algorithm_internal::c_end(input2); + for (; first1 != last1 && first2 != last2; + ++first1, (void)++first2, ++output) { + *output = binary_op(*first1, *first2); + } + + return output; } // c_replace() |