Skip to content

Commit bd0de71

Browse files
authored
Adds bounds-checking to the second range of absl container algorithms (#810)
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
1 parent b56cbdd commit bd0de71

File tree

2 files changed

+168
-39
lines changed

2 files changed

+168
-39
lines changed

absl/algorithm/container.h

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -340,24 +340,45 @@ container_algorithm_internal::ContainerDifferenceType<const C> c_count_if(
340340
// c_mismatch()
341341
//
342342
// Container-based version of the <algorithm> `std::mismatch()` function to
343-
// return the first element where two ordered containers differ.
343+
// return the first element where two ordered containers differ. Applies `==` to
344+
// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
344345
template <typename C1, typename C2>
345346
container_algorithm_internal::ContainerIterPairType<C1, C2>
346347
c_mismatch(C1& c1, C2& c2) {
347-
return std::mismatch(container_algorithm_internal::c_begin(c1),
348-
container_algorithm_internal::c_end(c1),
349-
container_algorithm_internal::c_begin(c2));
348+
auto first1 = container_algorithm_internal::c_begin(c1);
349+
auto last1 = container_algorithm_internal::c_end(c1);
350+
auto first2 = container_algorithm_internal::c_begin(c2);
351+
auto last2 = container_algorithm_internal::c_end(c2);
352+
353+
for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
354+
// Negates equality because Cpp17EqualityComparable doesn't require clients
355+
// to overload both `operator==` and `operator!=`.
356+
if (!(*first1 == *first2)) {
357+
break;
358+
}
359+
}
360+
361+
return std::make_pair(first1, first2);
350362
}
351363

352364
// Overload of c_mismatch() for using a predicate evaluation other than `==` as
353-
// the function's test condition.
365+
// the function's test condition. Applies `pred`to the first N elements of `c1`
366+
// and `c2`, where N = min(size(c1), size(c2)).
354367
template <typename C1, typename C2, typename BinaryPredicate>
355368
container_algorithm_internal::ContainerIterPairType<C1, C2>
356-
c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) {
357-
return std::mismatch(container_algorithm_internal::c_begin(c1),
358-
container_algorithm_internal::c_end(c1),
359-
container_algorithm_internal::c_begin(c2),
360-
std::forward<BinaryPredicate>(pred));
369+
c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) {
370+
auto first1 = container_algorithm_internal::c_begin(c1);
371+
auto last1 = container_algorithm_internal::c_end(c1);
372+
auto first2 = container_algorithm_internal::c_begin(c2);
373+
auto last2 = container_algorithm_internal::c_end(c2);
374+
375+
for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
376+
if (!pred(*first1, *first2)) {
377+
break;
378+
}
379+
}
380+
381+
return std::make_pair(first1, first2);
361382
}
362383

363384
// c_equal()
@@ -539,12 +560,20 @@ BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) {
539560
// c_swap_ranges()
540561
//
541562
// Container-based version of the <algorithm> `std::swap_ranges()` function to
542-
// swap a container's elements with another container's elements.
563+
// swap a container's elements with another container's elements. Swaps the
564+
// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
543565
template <typename C1, typename C2>
544566
container_algorithm_internal::ContainerIter<C2> c_swap_ranges(C1& c1, C2& c2) {
545-
return std::swap_ranges(container_algorithm_internal::c_begin(c1),
546-
container_algorithm_internal::c_end(c1),
547-
container_algorithm_internal::c_begin(c2));
567+
auto first1 = container_algorithm_internal::c_begin(c1);
568+
auto last1 = container_algorithm_internal::c_end(c1);
569+
auto first2 = container_algorithm_internal::c_begin(c2);
570+
auto last2 = container_algorithm_internal::c_end(c2);
571+
572+
using std::swap;
573+
for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
574+
swap(*first1, *first2);
575+
}
576+
return first2;
548577
}
549578

550579
// c_transform()
@@ -562,16 +591,23 @@ OutputIterator c_transform(const InputSequence& input, OutputIterator output,
562591
}
563592

564593
// Overload of c_transform() for performing a transformation using a binary
565-
// predicate.
594+
// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`,
595+
// where N = min(size(c1), size(c2)).
566596
template <typename InputSequence1, typename InputSequence2,
567597
typename OutputIterator, typename BinaryOp>
568598
OutputIterator c_transform(const InputSequence1& input1,
569599
const InputSequence2& input2, OutputIterator output,
570600
BinaryOp&& binary_op) {
571-
return std::transform(container_algorithm_internal::c_begin(input1),
572-
container_algorithm_internal::c_end(input1),
573-
container_algorithm_internal::c_begin(input2), output,
574-
std::forward<BinaryOp>(binary_op));
601+
auto first1 = container_algorithm_internal::c_begin(input1);
602+
auto last1 = container_algorithm_internal::c_end(input1);
603+
auto first2 = container_algorithm_internal::c_begin(input2);
604+
auto last2 = container_algorithm_internal::c_end(input2);
605+
for (; first1 != last1 && first2 != last2;
606+
++first1, (void)++first2, ++output) {
607+
*output = binary_op(*first1, *first2);
608+
}
609+
610+
return output;
575611
}
576612

577613
// c_replace()

absl/algorithm/container_test.cc

Lines changed: 113 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ class NonMutatingTest : public testing::Test {
5757
};
5858

5959
struct AccumulateCalls {
60-
void operator()(int value) {
61-
calls.push_back(value);
62-
}
60+
void operator()(int value) { calls.push_back(value); }
6361
std::vector<int> calls;
6462
};
6563

@@ -68,7 +66,6 @@ bool BinPredicate(int v1, int v2) { return v1 < v2; }
6866
bool Equals(int v1, int v2) { return v1 == v2; }
6967
bool IsOdd(int x) { return x % 2 != 0; }
7068

71-
7269
TEST_F(NonMutatingTest, Distance) {
7370
EXPECT_EQ(container_.size(), absl::c_distance(container_));
7471
EXPECT_EQ(sequence_.size(), absl::c_distance(sequence_));
@@ -151,13 +148,90 @@ TEST_F(NonMutatingTest, CountIf) {
151148
}
152149

153150
TEST_F(NonMutatingTest, Mismatch) {
154-
absl::c_mismatch(container_, sequence_);
155-
absl::c_mismatch(sequence_, container_);
151+
// Testing necessary as absl::c_mismatch executes logic.
152+
{
153+
auto result = absl::c_mismatch(vector_, sequence_);
154+
EXPECT_EQ(result.first, vector_.end());
155+
EXPECT_EQ(result.second, sequence_.end());
156+
}
157+
{
158+
auto result = absl::c_mismatch(sequence_, vector_);
159+
EXPECT_EQ(result.first, sequence_.end());
160+
EXPECT_EQ(result.second, vector_.end());
161+
}
162+
163+
sequence_.back() = 5;
164+
{
165+
auto result = absl::c_mismatch(vector_, sequence_);
166+
EXPECT_EQ(result.first, std::prev(vector_.end()));
167+
EXPECT_EQ(result.second, std::prev(sequence_.end()));
168+
}
169+
{
170+
auto result = absl::c_mismatch(sequence_, vector_);
171+
EXPECT_EQ(result.first, std::prev(sequence_.end()));
172+
EXPECT_EQ(result.second, std::prev(vector_.end()));
173+
}
174+
175+
sequence_.pop_back();
176+
{
177+
auto result = absl::c_mismatch(vector_, sequence_);
178+
EXPECT_EQ(result.first, std::prev(vector_.end()));
179+
EXPECT_EQ(result.second, sequence_.end());
180+
}
181+
{
182+
auto result = absl::c_mismatch(sequence_, vector_);
183+
EXPECT_EQ(result.first, sequence_.end());
184+
EXPECT_EQ(result.second, std::prev(vector_.end()));
185+
}
186+
{
187+
struct NoNotEquals {
188+
constexpr bool operator==(NoNotEquals) const { return true; }
189+
constexpr bool operator!=(NoNotEquals) const = delete;
190+
};
191+
std::vector<NoNotEquals> first;
192+
std::list<NoNotEquals> second;
193+
194+
// Check this still compiles.
195+
absl::c_mismatch(first, second);
196+
}
156197
}
157198

158199
TEST_F(NonMutatingTest, MismatchWithPredicate) {
159-
absl::c_mismatch(container_, sequence_, BinPredicate);
160-
absl::c_mismatch(sequence_, container_, BinPredicate);
200+
// Testing necessary as absl::c_mismatch executes logic.
201+
{
202+
auto result = absl::c_mismatch(vector_, sequence_, BinPredicate);
203+
EXPECT_EQ(result.first, vector_.begin());
204+
EXPECT_EQ(result.second, sequence_.begin());
205+
}
206+
{
207+
auto result = absl::c_mismatch(sequence_, vector_, BinPredicate);
208+
EXPECT_EQ(result.first, sequence_.begin());
209+
EXPECT_EQ(result.second, vector_.begin());
210+
}
211+
212+
sequence_.front() = 0;
213+
{
214+
auto result = absl::c_mismatch(vector_, sequence_, BinPredicate);
215+
EXPECT_EQ(result.first, vector_.begin());
216+
EXPECT_EQ(result.second, sequence_.begin());
217+
}
218+
{
219+
auto result = absl::c_mismatch(sequence_, vector_, BinPredicate);
220+
EXPECT_EQ(result.first, std::next(sequence_.begin()));
221+
EXPECT_EQ(result.second, std::next(vector_.begin()));
222+
}
223+
224+
sequence_.clear();
225+
{
226+
auto result = absl::c_mismatch(vector_, sequence_, BinPredicate);
227+
EXPECT_EQ(result.first, vector_.begin());
228+
EXPECT_EQ(result.second, sequence_.end());
229+
}
230+
{
231+
auto result = absl::c_mismatch(sequence_, vector_, BinPredicate);
232+
EXPECT_EQ(result.first, sequence_.end());
233+
EXPECT_EQ(result.second, vector_.begin());
234+
}
161235
}
162236

163237
TEST_F(NonMutatingTest, Equal) {
@@ -519,11 +593,9 @@ TEST_F(SortingTest, IsSortedUntil) {
519593
TEST_F(SortingTest, NthElement) {
520594
std::vector<int> unsorted = {2, 4, 1, 3};
521595
absl::c_nth_element(unsorted, unsorted.begin() + 2);
522-
EXPECT_THAT(unsorted,
523-
ElementsAre(Lt(3), Lt(3), 3, Gt(3)));
596+
EXPECT_THAT(unsorted, ElementsAre(Lt(3), Lt(3), 3, Gt(3)));
524597
absl::c_nth_element(unsorted, unsorted.begin() + 2, std::greater<int>());
525-
EXPECT_THAT(unsorted,
526-
ElementsAre(Gt(2), Gt(2), 2, Lt(2)));
598+
EXPECT_THAT(unsorted, ElementsAre(Gt(2), Gt(2), 2, Lt(2)));
527599
}
528600

529601
TEST(MutatingTest, IsPartitioned) {
@@ -676,6 +748,15 @@ TEST(MutatingTest, SwapRanges) {
676748
absl::c_swap_ranges(odds, evens);
677749
EXPECT_THAT(odds, ElementsAre(1, 3, 5));
678750
EXPECT_THAT(evens, ElementsAre(2, 4, 6));
751+
752+
odds.pop_back();
753+
absl::c_swap_ranges(odds, evens);
754+
EXPECT_THAT(odds, ElementsAre(2, 4));
755+
EXPECT_THAT(evens, ElementsAre(1, 3, 6));
756+
757+
absl::c_swap_ranges(evens, odds);
758+
EXPECT_THAT(odds, ElementsAre(1, 3));
759+
EXPECT_THAT(evens, ElementsAre(2, 4, 6));
679760
}
680761

681762
TEST_F(NonMutatingTest, Transform) {
@@ -690,6 +771,20 @@ TEST_F(NonMutatingTest, Transform) {
690771
EXPECT_EQ(std::vector<int>({1, 5, 4}), z);
691772
*end = 7;
692773
EXPECT_EQ(std::vector<int>({1, 5, 4, 7}), z);
774+
775+
z.clear();
776+
y.pop_back();
777+
end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
778+
EXPECT_EQ(std::vector<int>({1, 5}), z);
779+
*end = 7;
780+
EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
781+
782+
z.clear();
783+
std::swap(x, y);
784+
end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
785+
EXPECT_EQ(std::vector<int>({1, 5}), z);
786+
*end = 7;
787+
EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
693788
}
694789

695790
TEST(MutatingTest, Replace) {
@@ -755,21 +850,19 @@ MATCHER_P2(IsElement, key, value, "") {
755850
TEST(MutatingTest, StableSort) {
756851
std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
757852
absl::c_stable_sort(test_vector);
758-
EXPECT_THAT(
759-
test_vector,
760-
ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
761-
IsElement(2, 0), IsElement(2, 2)));
853+
EXPECT_THAT(test_vector,
854+
ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
855+
IsElement(2, 0), IsElement(2, 2)));
762856
}
763857

764858
TEST(MutatingTest, StableSortWithPredicate) {
765859
std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
766860
absl::c_stable_sort(test_vector, [](const Element& e1, const Element& e2) {
767861
return e2 < e1;
768862
});
769-
EXPECT_THAT(
770-
test_vector,
771-
ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
772-
IsElement(1, 1), IsElement(1, 0)));
863+
EXPECT_THAT(test_vector,
864+
ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
865+
IsElement(1, 1), IsElement(1, 0)));
773866
}
774867

775868
TEST(MutatingTest, ReplaceCopyIf) {

0 commit comments

Comments
 (0)