Skip to content

Commit 6994792

Browse files
authored
Adds bounds-checking to the second range of absl container algorithms (#812)
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 aa84489 commit 6994792

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
@@ -327,24 +327,45 @@ container_algorithm_internal::ContainerDifferenceType<const C> c_count_if(
327327
// c_mismatch()
328328
//
329329
// Container-based version of the <algorithm> `std::mismatch()` function to
330-
// return the first element where two ordered containers differ.
330+
// return the first element where two ordered containers differ. Applies `==` to
331+
// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
331332
template <typename C1, typename C2>
332333
container_algorithm_internal::ContainerIterPairType<C1, C2>
333334
c_mismatch(C1& c1, C2& c2) {
334-
return std::mismatch(container_algorithm_internal::c_begin(c1),
335-
container_algorithm_internal::c_end(c1),
336-
container_algorithm_internal::c_begin(c2));
335+
auto first1 = container_algorithm_internal::c_begin(c1);
336+
auto last1 = container_algorithm_internal::c_end(c1);
337+
auto first2 = container_algorithm_internal::c_begin(c2);
338+
auto last2 = container_algorithm_internal::c_end(c2);
339+
340+
for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
341+
// Negates equality because Cpp17EqualityComparable doesn't require clients
342+
// to overload both `operator==` and `operator!=`.
343+
if (!(*first1 == *first2)) {
344+
break;
345+
}
346+
}
347+
348+
return std::make_pair(first1, first2);
337349
}
338350

339351
// Overload of c_mismatch() for using a predicate evaluation other than `==` as
340-
// the function's test condition.
352+
// the function's test condition. Applies `pred`to the first N elements of `c1`
353+
// and `c2`, where N = min(size(c1), size(c2)).
341354
template <typename C1, typename C2, typename BinaryPredicate>
342355
container_algorithm_internal::ContainerIterPairType<C1, C2>
343-
c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) {
344-
return std::mismatch(container_algorithm_internal::c_begin(c1),
345-
container_algorithm_internal::c_end(c1),
346-
container_algorithm_internal::c_begin(c2),
347-
std::forward<BinaryPredicate>(pred));
356+
c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) {
357+
auto first1 = container_algorithm_internal::c_begin(c1);
358+
auto last1 = container_algorithm_internal::c_end(c1);
359+
auto first2 = container_algorithm_internal::c_begin(c2);
360+
auto last2 = container_algorithm_internal::c_end(c2);
361+
362+
for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
363+
if (!pred(*first1, *first2)) {
364+
break;
365+
}
366+
}
367+
368+
return std::make_pair(first1, first2);
348369
}
349370

350371
// c_equal()
@@ -524,12 +545,20 @@ BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) {
524545
// c_swap_ranges()
525546
//
526547
// Container-based version of the <algorithm> `std::swap_ranges()` function to
527-
// swap a container's elements with another container's elements.
548+
// swap a container's elements with another container's elements. Swaps the
549+
// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
528550
template <typename C1, typename C2>
529551
container_algorithm_internal::ContainerIter<C2> c_swap_ranges(C1& c1, C2& c2) {
530-
return std::swap_ranges(container_algorithm_internal::c_begin(c1),
531-
container_algorithm_internal::c_end(c1),
532-
container_algorithm_internal::c_begin(c2));
552+
auto first1 = container_algorithm_internal::c_begin(c1);
553+
auto last1 = container_algorithm_internal::c_end(c1);
554+
auto first2 = container_algorithm_internal::c_begin(c2);
555+
auto last2 = container_algorithm_internal::c_end(c2);
556+
557+
using std::swap;
558+
for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
559+
swap(*first1, *first2);
560+
}
561+
return first2;
533562
}
534563

535564
// c_transform()
@@ -547,16 +576,23 @@ OutputIterator c_transform(const InputSequence& input, OutputIterator output,
547576
}
548577

549578
// Overload of c_transform() for performing a transformation using a binary
550-
// predicate.
579+
// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`,
580+
// where N = min(size(c1), size(c2)).
551581
template <typename InputSequence1, typename InputSequence2,
552582
typename OutputIterator, typename BinaryOp>
553583
OutputIterator c_transform(const InputSequence1& input1,
554584
const InputSequence2& input2, OutputIterator output,
555585
BinaryOp&& binary_op) {
556-
return std::transform(container_algorithm_internal::c_begin(input1),
557-
container_algorithm_internal::c_end(input1),
558-
container_algorithm_internal::c_begin(input2), output,
559-
std::forward<BinaryOp>(binary_op));
586+
auto first1 = container_algorithm_internal::c_begin(input1);
587+
auto last1 = container_algorithm_internal::c_end(input1);
588+
auto first2 = container_algorithm_internal::c_begin(input2);
589+
auto last2 = container_algorithm_internal::c_end(input2);
590+
for (; first1 != last1 && first2 != last2;
591+
++first1, (void)++first2, ++output) {
592+
*output = binary_op(*first1, *first2);
593+
}
594+
595+
return output;
560596
}
561597

562598
// 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) {
@@ -513,11 +587,9 @@ TEST_F(SortingTest, IsSortedUntil) {
513587
TEST_F(SortingTest, NthElement) {
514588
std::vector<int> unsorted = {2, 4, 1, 3};
515589
absl::c_nth_element(unsorted, unsorted.begin() + 2);
516-
EXPECT_THAT(unsorted,
517-
ElementsAre(Lt(3), Lt(3), 3, Gt(3)));
590+
EXPECT_THAT(unsorted, ElementsAre(Lt(3), Lt(3), 3, Gt(3)));
518591
absl::c_nth_element(unsorted, unsorted.begin() + 2, std::greater<int>());
519-
EXPECT_THAT(unsorted,
520-
ElementsAre(Gt(2), Gt(2), 2, Lt(2)));
592+
EXPECT_THAT(unsorted, ElementsAre(Gt(2), Gt(2), 2, Lt(2)));
521593
}
522594

523595
TEST(MutatingTest, IsPartitioned) {
@@ -670,6 +742,15 @@ TEST(MutatingTest, SwapRanges) {
670742
absl::c_swap_ranges(odds, evens);
671743
EXPECT_THAT(odds, ElementsAre(1, 3, 5));
672744
EXPECT_THAT(evens, ElementsAre(2, 4, 6));
745+
746+
odds.pop_back();
747+
absl::c_swap_ranges(odds, evens);
748+
EXPECT_THAT(odds, ElementsAre(2, 4));
749+
EXPECT_THAT(evens, ElementsAre(1, 3, 6));
750+
751+
absl::c_swap_ranges(evens, odds);
752+
EXPECT_THAT(odds, ElementsAre(1, 3));
753+
EXPECT_THAT(evens, ElementsAre(2, 4, 6));
673754
}
674755

675756
TEST_F(NonMutatingTest, Transform) {
@@ -684,6 +765,20 @@ TEST_F(NonMutatingTest, Transform) {
684765
EXPECT_EQ(std::vector<int>({1, 5, 4}), z);
685766
*end = 7;
686767
EXPECT_EQ(std::vector<int>({1, 5, 4, 7}), z);
768+
769+
z.clear();
770+
y.pop_back();
771+
end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
772+
EXPECT_EQ(std::vector<int>({1, 5}), z);
773+
*end = 7;
774+
EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
775+
776+
z.clear();
777+
std::swap(x, y);
778+
end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
779+
EXPECT_EQ(std::vector<int>({1, 5}), z);
780+
*end = 7;
781+
EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
687782
}
688783

689784
TEST(MutatingTest, Replace) {
@@ -749,21 +844,19 @@ MATCHER_P2(IsElement, key, value, "") {
749844
TEST(MutatingTest, StableSort) {
750845
std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
751846
absl::c_stable_sort(test_vector);
752-
EXPECT_THAT(
753-
test_vector,
754-
ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
755-
IsElement(2, 0), IsElement(2, 2)));
847+
EXPECT_THAT(test_vector,
848+
ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
849+
IsElement(2, 0), IsElement(2, 2)));
756850
}
757851

758852
TEST(MutatingTest, StableSortWithPredicate) {
759853
std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
760854
absl::c_stable_sort(test_vector, [](const Element& e1, const Element& e2) {
761855
return e2 < e1;
762856
});
763-
EXPECT_THAT(
764-
test_vector,
765-
ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
766-
IsElement(1, 1), IsElement(1, 0)));
857+
EXPECT_THAT(test_vector,
858+
ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
859+
IsElement(1, 1), IsElement(1, 0)));
767860
}
768861

769862
TEST(MutatingTest, ReplaceCopyIf) {

0 commit comments

Comments
 (0)