Skip to content

Commit 417ef68

Browse files
Prevent external construction of checked iterators (#1263)
* Prevent external construction of checked iterators Keep iterator state constructors private to span and dyn_array so callers cannot fabricate bounds metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * clang-format --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b4fa05c commit 417ef68

4 files changed

Lines changed: 52 additions & 14 deletions

File tree

include/gsl/dyn_array

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434

3535
namespace gsl
3636
{
37+
template <typename T, typename Allocator = std::allocator<T>>
38+
class dyn_array;
39+
3740
namespace details
3841
{
3942
template <typename T, typename Allocator = std::allocator<T>>
@@ -178,13 +181,6 @@ namespace details
178181
constexpr dyn_array_iterator() = default;
179182
#endif /* __cpp_lib_ranges >= 201911L */
180183

181-
constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos)
182-
: _ptr{ptr}, _pos{pos}, _end_pos{end_pos}
183-
{
184-
Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0));
185-
Ensures(_pos <= _end_pos);
186-
}
187-
188184
constexpr operator dyn_array_iterator<const T>() const { return {_ptr, _pos, _end_pos}; }
189185

190186
#if defined(_MSC_VER) && defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
@@ -281,13 +277,23 @@ namespace details
281277
}
282278

283279
private:
280+
constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos)
281+
: _ptr{ptr}, _pos{pos}, _end_pos{end_pos}
282+
{
283+
Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0));
284+
Ensures(_pos <= _end_pos);
285+
}
286+
284287
pointer _ptr{};
285288
size_type _pos{};
286289
size_type _end_pos{};
290+
291+
template <typename, typename>
292+
friend class ::gsl::dyn_array;
287293
};
288294
} // namespace details
289295

290-
template <typename T, typename Allocator = std::allocator<T>>
296+
template <typename T, typename Allocator>
291297
class dyn_array : private details::dyn_array_base<T, Allocator>
292298
{
293299
using base = details::dyn_array_base<T, Allocator>;

include/gsl/span

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,6 @@ namespace details
139139
#endif // _MSC_VER
140140
constexpr span_iterator() = default;
141141

142-
constexpr span_iterator(pointer begin, pointer end, pointer current)
143-
: begin_(begin), end_(end), current_(current)
144-
{
145-
Expects(begin_ <= current_ && current <= end_);
146-
}
147-
148142
constexpr operator span_iterator<const Type>() const noexcept
149143
{
150144
return {begin_, end_, current_};
@@ -335,10 +329,21 @@ namespace details
335329
}
336330
#endif
337331

332+
private:
333+
constexpr span_iterator(pointer begin, pointer end, pointer current)
334+
: begin_(begin), end_(end), current_(current)
335+
{
336+
Expects(begin_ <= current_ && current <= end_);
337+
}
338+
338339
pointer begin_ = nullptr;
339340
pointer end_ = nullptr;
340341
pointer current_ = nullptr;
341342

343+
template <class>
344+
friend class span_iterator;
345+
template <class, std::size_t>
346+
friend class ::gsl::span;
342347
template <typename Ptr>
343348
friend struct std::pointer_traits;
344349
};

tests/dyn_array_tests.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ static_assert(sizeof(gsl::dyn_array<int>) == 2 * sizeof(void*),
1919
static_assert(
2020
std::is_convertible<gsl::dyn_array<int>::iterator, gsl::dyn_array<int>::const_iterator>::value,
2121
"gsl::dyn_array iterator should be implicitly convertible to const_iterator");
22+
static_assert(!std::is_constructible<gsl::dyn_array<int>::iterator, gsl::dyn_array<int>&>::value,
23+
"dyn_array<int>::iterator should not be constructible from dyn_array<int>");
24+
static_assert(
25+
!std::is_constructible<gsl::dyn_array<int>::iterator, int*, std::size_t, std::size_t>::value,
26+
"dyn_array<int>::iterator should not be constructible from an arbitrary state triple");
27+
static_assert(
28+
!std::is_constructible<gsl::dyn_array<int>::const_iterator, const gsl::dyn_array<int>&>::value,
29+
"dyn_array<int>::const_iterator should not be constructible from dyn_array<int>");
30+
static_assert(!std::is_constructible<gsl::dyn_array<int>::const_iterator, const int*, std::size_t,
31+
std::size_t>::value,
32+
"dyn_array<int>::const_iterator should not be constructible from an arbitrary state "
33+
"triple");
34+
static_assert(std::is_copy_constructible<gsl::dyn_array<int>::iterator>::value,
35+
"dyn_array<int>::iterator should remain copy constructible");
2236

2337
#if defined(__cpp_lib_concepts) && (__cpp_lib_concepts >= 202002L)
2438
static_assert(std::input_iterator<gsl::dyn_array<int>::iterator>,

tests/span_compatibility_tests.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,19 @@ static_assert(std::is_trivially_copyable<gsl::span<const int, 3>>::value,
654654
static_assert(std::is_trivially_copyable<gsl::span<const int, 3>::iterator>::value,
655655
"span<const int, 3>::iterator should be trivially copyable");
656656

657+
static_assert(!std::is_constructible<gsl::span<int>::iterator, gsl::span<int>>::value,
658+
"span<int>::iterator should not be constructible from span<int>");
659+
static_assert(!std::is_constructible<gsl::span<int>::iterator, int*, int*, int*>::value,
660+
"span<int>::iterator should not be constructible from an arbitrary pointer triple");
661+
static_assert(!std::is_constructible<gsl::span<const int>::iterator, gsl::span<const int>>::value,
662+
"span<const int>::iterator should not be constructible from span<const int>");
663+
static_assert(
664+
!std::is_constructible<gsl::span<const int>::iterator, const int*, const int*,
665+
const int*>::value,
666+
"span<const int>::iterator should not be constructible from an arbitrary pointer triple");
667+
static_assert(std::is_copy_constructible<gsl::span<int>::iterator>::value,
668+
"span<int>::iterator should remain copy constructible");
669+
657670
// nothrow constructible assertions
658671
static_assert(std::is_nothrow_constructible<gsl::span<int>, int*, std::size_t>::value,
659672
"std::is_nothrow_constructible<gsl::span<int>, int*, std::size_t>");

0 commit comments

Comments
 (0)