Skip to content

Commit 49bd3cc

Browse files
authored
Adds bounds-checking to the second range of absl container algorithms (#813)
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 fcb1045 commit 49bd3cc

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

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

351372
// c_equal()
@@ -515,12 +536,20 @@ OutputIterator c_move(C&& src, OutputIterator dest) {
515536
// c_swap_ranges()
516537
//
517538
// Container-based version of the <algorithm> `std::swap_ranges()` function to
518-
// swap a container's elements with another container's elements.
539+
// swap a container's elements with another container's elements. Swaps the
540+
// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
519541
template <typename C1, typename C2>
520542
container_algorithm_internal::ContainerIter<C2> c_swap_ranges(C1& c1, C2& c2) {
521-
return std::swap_ranges(container_algorithm_internal::c_begin(c1),
522-
container_algorithm_internal::c_end(c1),
523-
container_algorithm_internal::c_begin(c2));
543+
auto first1 = container_algorithm_internal::c_begin(c1);
544+
auto last1 = container_algorithm_internal::c_end(c1);
545+
auto first2 = container_algorithm_internal::c_begin(c2);
546+
auto last2 = container_algorithm_internal::c_end(c2);
547+
548+
using std::swap;
549+
for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
550+
swap(*first1, *first2);
551+
}
552+
return first2;
524553
}
525554

526555
// c_transform()
@@ -538,16 +567,23 @@ OutputIterator c_transform(const InputSequence& input, OutputIterator output,
538567
}
539568

540569
// Overload of c_transform() for performing a transformation using a binary
541-
// predicate.
570+
// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`,
571+
// where N = min(size(c1), size(c2)).
542572
template <typename InputSequence1, typename InputSequence2,
543573
typename OutputIterator, typename BinaryOp>
544574
OutputIterator c_transform(const InputSequence1& input1,
545575
const InputSequence2& input2, OutputIterator output,
546576
BinaryOp&& binary_op) {
547-
return std::transform(container_algorithm_internal::c_begin(input1),
548-
container_algorithm_internal::c_end(input1),
549-
container_algorithm_internal::c_begin(input2), output,
550-
std::forward<BinaryOp>(binary_op));
577+
auto first1 = container_algorithm_internal::c_begin(input1);
578+
auto last1 = container_algorithm_internal::c_end(input1);
579+
auto first2 = container_algorithm_internal::c_begin(input2);
580+
auto last2 = container_algorithm_internal::c_end(input2);
581+
for (; first1 != last1 && first2 != last2;
582+
++first1, (void)++first2, ++output) {
583+
*output = binary_op(*first1, *first2);
584+
}
585+
586+
return output;
551587
}
552588

553589
// 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) {
@@ -657,6 +729,15 @@ TEST(MutatingTest, SwapRanges) {
657729
absl::c_swap_ranges(odds, evens);
658730
EXPECT_THAT(odds, ElementsAre(1, 3, 5));
659731
EXPECT_THAT(evens, ElementsAre(2, 4, 6));
732+
733+
odds.pop_back();
734+
absl::c_swap_ranges(odds, evens);
735+
EXPECT_THAT(odds, ElementsAre(2, 4));
736+
EXPECT_THAT(evens, ElementsAre(1, 3, 6));
737+
738+
absl::c_swap_ranges(evens, odds);
739+
EXPECT_THAT(odds, ElementsAre(1, 3));
740+
EXPECT_THAT(evens, ElementsAre(2, 4, 6));
660741
}
661742

662743
TEST_F(NonMutatingTest, Transform) {
@@ -671,6 +752,20 @@ TEST_F(NonMutatingTest, Transform) {
671752
EXPECT_EQ(std::vector<int>({1, 5, 4}), z);
672753
*end = 7;
673754
EXPECT_EQ(std::vector<int>({1, 5, 4, 7}), z);
755+
756+
z.clear();
757+
y.pop_back();
758+
end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
759+
EXPECT_EQ(std::vector<int>({1, 5}), z);
760+
*end = 7;
761+
EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
762+
763+
z.clear();
764+
std::swap(x, y);
765+
end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
766+
EXPECT_EQ(std::vector<int>({1, 5}), z);
767+
*end = 7;
768+
EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
674769
}
675770

676771
TEST(MutatingTest, Replace) {
@@ -736,21 +831,19 @@ MATCHER_P2(IsElement, key, value, "") {
736831
TEST(MutatingTest, StableSort) {
737832
std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
738833
absl::c_stable_sort(test_vector);
739-
EXPECT_THAT(
740-
test_vector,
741-
ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
742-
IsElement(2, 0), IsElement(2, 2)));
834+
EXPECT_THAT(test_vector,
835+
ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
836+
IsElement(2, 0), IsElement(2, 2)));
743837
}
744838

745839
TEST(MutatingTest, StableSortWithPredicate) {
746840
std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
747841
absl::c_stable_sort(test_vector, [](const Element& e1, const Element& e2) {
748842
return e2 < e1;
749843
});
750-
EXPECT_THAT(
751-
test_vector,
752-
ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
753-
IsElement(1, 1), IsElement(1, 0)));
844+
EXPECT_THAT(test_vector,
845+
ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
846+
IsElement(1, 1), IsElement(1, 0)));
754847
}
755848

756849
TEST(MutatingTest, ReplaceCopyIf) {

0 commit comments

Comments
 (0)