This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3686. In `lazy_split_view`, comparing a default-constructed `outer-iterator` or `inner-iterator` with `std::default_sentinel` results in null pointer dereference

Section: 26.7.16.3 [range.lazy.split.outer], 26.7.16.5 [range.lazy.split.inner] Status: New Submitter: Konstantin Varlamov Opened: 2022-03-23 Last modified: 2022-05-17

Priority: 3

View all issues with New status.

Discussion:

The internal iterator types `outer-iterator` and `inner-iterator` of `lazy_split_view` are default-constructible, but trying to compare a default-constructed instance of either of these classes to `std::default_sentinel` results in null pointer dereference (and, in all likelihood, a crash), as demonstrated in this demo link:

>     // Assuming `OuterIter` is an alias for `outer-iterator` of
>     // some `lazy_split_view` instantiation.
>     OuterIter o;
>     o == std::default_sentinel; // Null pointer dereference

>     InnerIter i; // Similar to `OuterIter` above.
>     i == std::default_sentinel; // Null pointer dereference

This is due to unchecked pointer access in the implementation of `outer-iterator` (26.7.16.3 [range.lazy.split.outer] p8):

>     return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;

(`parent_` is null for a default-constructed iterator `x`, making the access to `base_` invalid)

And similarly for `inner-iterator` (26.7.16.5 [range.lazy.split.inner] p7):

>     auto [pcur, pend] = subrange{x.i_.parent_->pattern_};

(For a default-constructed `inner-iterator x`, `i_` is a default-constructed `outer-iterator` member variable and `i_.parent_` is null, making the access to `pattern_` invalid)

It seems a reasonable expectation for users to expect comparing a default-constructed iterator to `std::default_sentinel` to be a well-defined operation that returns `true`. Alternatively, the corresponding `operator==` functions should add a non-normative note stating that the iterator cannot be default-constructed.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll. Three votes for NAD.

Proposed resolution:

This wording is relative to N4910.

1. Modify 26.7.16.3 [range.lazy.split.outer] as indicated:

   >     friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);

   > > -8- Effects: Equivalent to:

   > > >     if (!x.parent_) return true;
   > > >     return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;

2. Modify 26.7.16.5 [range.lazy.split.inner], as indicated:

   >     friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t);

   > > -7- Effects: Equivalent to:

   > > >     if (!x.i_.parent_) return true;
   > > >     auto [pcur, pend] = subrange{x.i_.parent_->pattern_};
   > > >     […]