Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<algorithm>: Fix bogus pointer arithmetic with integer-class #5091

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 27 additions & 22 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -6600,15 +6600,19 @@ namespace ranges {

if (_Count1 <= _Count2 && _Count1 <= _Capacity) { // buffer left range, then move parts
_Uninitialized_backout<iter_value_t<_It>*> _Backout{
_Temp_ptr, _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Temp_ptr, _Temp_ptr + _Count1).out};
_Temp_ptr, _RANGES _Uninitialized_move_unchecked(
_First, _Mid, _Temp_ptr, _Temp_ptr + static_cast<ptrdiff_t>(_Count1))
.out};
const _It _New_mid = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)).out;
_RANGES _Move_unchecked(_Backout._First, _Backout._Last, _New_mid);
return _New_mid;
}

if (_Count2 <= _Capacity) { // buffer right range, then move parts
_Uninitialized_backout<iter_value_t<_It>*> _Backout{
_Temp_ptr, _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Temp_ptr, _Temp_ptr + _Count2).out};
_Temp_ptr, _RANGES _Uninitialized_move_unchecked(
_Mid, _Last, _Temp_ptr, _Temp_ptr + static_cast<ptrdiff_t>(_Count2))
.out};
_RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last));
return _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _STD move(_First)).out;
}
Expand Down Expand Up @@ -8888,8 +8892,10 @@ namespace ranges {
const iter_difference_t<_It> _Half_count_ceil = _Count - _Half_count;
const _It _Mid = _First + _Half_count_ceil;
if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer
_Buffered_merge_sort_common(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Pred, _Proj);
_Buffered_merge_sort_common(_Mid, _Last, _Half_count, _Temp_ptr, _Pred, _Proj);
_Buffered_merge_sort_common(
_First, _Mid, static_cast<ptrdiff_t>(_Half_count_ceil), _Temp_ptr, _Pred, _Proj);
_Buffered_merge_sort_common(
_Mid, _Last, static_cast<ptrdiff_t>(_Half_count), _Temp_ptr, _Pred, _Proj);
} else { // temp buffer not big enough, divide and conquer
_Stable_sort_common_buffered(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Capacity, _Pred, _Proj);
_Stable_sort_common_buffered(_Mid, _Last, _Half_count, _Temp_ptr, _Capacity, _Pred, _Proj);
Expand All @@ -8901,24 +8907,24 @@ namespace ranges {
}

template <class _It, class _Pr, class _Pj>
static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const iter_difference_t<_It> _Count,
static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const ptrdiff_t _Count,
iter_value_t<_It>* const _Temp_ptr, _Pr _Pred, _Pj _Proj) {
// sort using temp buffer for merges
// pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t
// pre: _Count <= capacity of buffer at _Temp_ptr
_STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>);
_STL_INTERNAL_CHECK(_Last - _First == _Count);

_Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred, _Proj);
// merge adjacent pairs of chunks to and from temp buffer
if (_Count <= _Isort_max<_It>) {
if (_Count <= _ISORT_MAX) {
return;
}

// do the first merge, constructing elements in the temporary buffer
_Uninitialized_chunked_merge_common(_First, _Last, _Temp_ptr, _Count, _Pred, _Proj);
_Uninitialized_backout<iter_value_t<_It>*> _Backout{_Temp_ptr, _Temp_ptr + _Count};
iter_difference_t<_It> _Chunk_size = _Isort_max<_It>;
ptrdiff_t _Chunk_size = _ISORT_MAX;
for (;;) {
// unconditionally merge elements back into the source buffer
_Chunk_size <<= 1;
Expand All @@ -8934,14 +8940,13 @@ namespace ranges {
}

template <class _It, class _Pr, class _Pj>
static void _Insertion_sort_isort_max_chunks(
_It _First, _It _Last, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) {
static void _Insertion_sort_isort_max_chunks(_It _First, _It _Last, ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) {
// insertion sort every chunk of distance _Isort_max<_It> in [_First, _Last)
_STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>);
_STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count);

for (; _Isort_max<_It> < _Count; _Count -= _Isort_max<_It>) { // sort chunks
for (; _ISORT_MAX < _Count; _Count -= _ISORT_MAX) { // sort chunks
_First = _RANGES _Insertion_sort_common(_First, _First + _Isort_max<_It>, _Pred, _Proj);
}

Expand All @@ -8950,8 +8955,8 @@ namespace ranges {
}

template <class _It, class _Pr, class _Pj>
static void _Uninitialized_chunked_merge_common(_It _First, const _It _Last, iter_value_t<_It>* const _Dest,
iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) {
static void _Uninitialized_chunked_merge_common(
_It _First, const _It _Last, iter_value_t<_It>* const _Dest, ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) {
// move to uninitialized merging adjacent chunks of distance _Isort_max<_It>
_STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>);
Expand All @@ -8960,14 +8965,14 @@ namespace ranges {

_Uninitialized_backout<iter_value_t<_It>*> _Backout{_Dest};
const auto _Backout_end = _Dest + _Count;
while (_Isort_max<_It> < _Count) {
_Count -= _Isort_max<_It>;
const auto _Chunk2 = (_STD min)(_Isort_max<_It>, _Count);
while (_ISORT_MAX < _Count) {
_Count -= _ISORT_MAX;
const auto _Chunk2 = (_STD min)(static_cast<ptrdiff_t>(_ISORT_MAX), _Count);
_Count -= _Chunk2;

auto _Mid1 = _First + _Isort_max<_It>;
auto _Last1 = _Mid1 + _Chunk2;
auto _Last2 = _Backout._Last + _Isort_max<_It> + _Chunk2;
auto _Last1 = _Mid1 + static_cast<iter_difference_t<_It>>(_Chunk2);
auto _Last2 = _Backout._Last + _ISORT_MAX + _Chunk2;
_Backout._Last = _Uninitialized_merge_move(
_STD move(_First), _STD move(_Mid1), _Last1, _Backout._Last, _Last2, _Pred, _Proj);
_First = _STD move(_Last1);
Expand Down Expand Up @@ -9047,8 +9052,8 @@ namespace ranges {
}

template <class _It1, class _It2, class _Pr, class _Pj>
static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest,
const iter_difference_t<_It1> _Chunk_size, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj _Proj) {
static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest, const ptrdiff_t _Chunk_size,
ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) {
// move merging adjacent chunks of distance _Chunk_size
_STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It1>);
_STL_INTERNAL_STATIC_ASSERT(sortable<_It1, _Pr, _Pj>);
Expand All @@ -9061,8 +9066,8 @@ namespace ranges {
const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count);
_Count -= _Right_chunk_size;

auto _Mid1 = _First + _Chunk_size;
auto _Last1 = _Mid1 + _Right_chunk_size;
auto _Mid1 = _First + static_cast<iter_difference_t<_It1>>(_Chunk_size);
auto _Last1 = _Mid1 + static_cast<iter_difference_t<_It1>>(_Right_chunk_size);
_Dest = _Merge_move_common(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj);
_First = _STD move(_Last1);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1163,10 +1163,10 @@ namespace test {
if constexpr (is_sized) {
const auto sz = to_unsigned(static_cast<Diff>(ranges::distance(r)));
return ranges::subrange<rediff_iter, rediff_sent, ranges::subrange_kind::sized>{
rediff_iter{r.begin()}, rediff_sent{r.end()}, sz};
rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}, sz};
} else {
return ranges::subrange<rediff_iter, rediff_sent, ranges::subrange_kind::unsized>{
rediff_iter{r.begin()}, rediff_sent{r.end()}};
rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}};
}
}
} // namespace test
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ tests\GH_002711_Zc_alignedNew-
tests\GH_002760_syncstream_memory_leak
tests\GH_002769_handle_deque_block_pointers
tests\GH_002789_Hash_vec_Tidy
tests\GH_002885_stable_sort_difference_type
tests\GH_002989_nothrow_unwrappable
tests\GH_002992_unwrappable_iter_sent_pairs
tests\GH_003003_format_decimal_point
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_20_matrix.lst
99 changes: 99 additions & 0 deletions tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <ranges>

#include "range_algorithm_support.hpp"

using namespace std;

constexpr auto pred = [](int i) { return i <= 42; };

template <class I>
void test_iota_transform() {
constexpr int orig[]{42, 1729};
int a[]{42, 1729};
auto vw = views::iota(I{}, static_cast<I>(ranges::size(a)))
| views::transform([&a](I i) -> auto& { return a[static_cast<size_t>(i)]; });

static_assert(three_way_comparable<ranges::iterator_t<ranges::iota_view<I>>>); // TRANSITION, /permissive
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

ranges::stable_sort(vw);
assert(ranges::equal(a, orig));

ranges::stable_sort(vw.begin(), vw.end());
assert(ranges::equal(a, orig));

ranges::inplace_merge(vw, ranges::next(vw.begin()));
assert(ranges::equal(a, orig));
ranges::inplace_merge(vw.begin(), ranges::next(vw.begin()), vw.end());
assert(ranges::equal(a, orig));

ranges::stable_partition(vw, pred);
assert(ranges::equal(a, orig));
ranges::stable_partition(vw.begin(), vw.end(), pred);
assert(ranges::equal(a, orig));
}

void test_iota_transform_all() {
test_iota_transform<signed char>();
test_iota_transform<short>();
test_iota_transform<int>();
test_iota_transform<long>();
test_iota_transform<long long>();

test_iota_transform<unsigned char>();
test_iota_transform<unsigned short>();
test_iota_transform<unsigned int>();
test_iota_transform<unsigned long>();
test_iota_transform<unsigned long long>();

test_iota_transform<char>();
#ifdef __cpp_char8_t
test_iota_transform<char8_t>();
#endif // defined(__cpp_char8_t)
test_iota_transform<char16_t>();
test_iota_transform<char32_t>();
test_iota_transform<wchar_t>();
}

template <class I>
void test_redifference() {
constexpr int orig[]{42, 1729};
int a[]{42, 1729};
auto vw = test::make_redifference_subrange<I>(a);

ranges::stable_sort(vw);
assert(ranges::equal(a, orig));

ranges::stable_sort(vw.begin(), vw.end());
assert(ranges::equal(a, orig));

ranges::inplace_merge(vw, ranges::next(vw.begin()));
assert(ranges::equal(a, orig));
ranges::inplace_merge(vw.begin(), ranges::next(vw.begin()), vw.end());
assert(ranges::equal(a, orig));

ranges::stable_partition(vw, pred);
assert(ranges::equal(a, orig));
ranges::stable_partition(vw.begin(), vw.end(), pred);
assert(ranges::equal(a, orig));
}

void test_redifference_all() {
test_redifference<signed char>();
test_redifference<short>();
test_redifference<int>();
test_redifference<long>();
test_redifference<long long>();
test_redifference<_Signed128>();
}

int main() {
test_iota_transform_all();
test_redifference_all();
}