Skip to content

Commit

Permalink
<algorithm>: Fix bogus pointer arithmetic with integer-class (micro…
Browse files Browse the repository at this point in the history
…soft#5091)

Co-authored-by: Casey Carter <cacarter@microsoft.com>
  • Loading branch information
frederick-vs-ja and CaseyCarter authored Nov 19, 2024
1 parent 1711bc3 commit fec1c8b
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 24 deletions.
49 changes: 27 additions & 22 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -6568,15 +6568,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 @@ -8856,8 +8860,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 @@ -8869,24 +8875,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 @@ -8902,14 +8908,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 @@ -8918,8 +8923,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 @@ -8928,14 +8933,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 @@ -9015,8 +9020,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 @@ -9029,8 +9034,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
4 changes: 4 additions & 0 deletions tests/std/tests/GH_002885_stable_sort_difference_type/env.lst
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

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();
}

0 comments on commit fec1c8b

Please sign in to comment.