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

Fix panic in min_width_1/grapheme_aligned when Range extends past bounds #2090

Closed
wants to merge 2 commits into from

Conversation

TylerAldrich
Copy link

This change addresses a bug when using jump_forward / jump_backwards, when the saved selection in the jumplist contains a range that extends past the end of the document.

The easiest way to reproduce this bug is to write "hi", save the space after "hi" to the jumplist via ctrl+s, delete everything, use ctrl+o to jump and you receive

thread 'main' panicked at 'assertion failed: char_idx <= slice.len_chars()', helix-core/src/graphemes.rs:37:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

because the range extends past the slice length.

I am very unfamiliar with this codebase, and I did add some tests and can confirm that after my changes, the use case I described now no longer panics, but if this is not the correct place to implement this bounds checking, please let me know.

Copy link
Contributor

@cessen cessen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a reasonable place to fix this. But I am a little concerned that clamping like this might end up masking out-of-bounds bugs that should otherwise be fixed.

For example, although I haven't looked closely at all, there's a chance that this change is actually just masking the bug you're running into, which may also manifest in other ways than out-of-bounds.

Comment on lines 549 to 550
// 4. Ranges are sorted by their position in the text.
pub fn ensure_invariants(self, text: RopeSlice) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to add the check here, let's add it to the list of checks in the comment as well.

/// they are set to the length of the slice.
#[must_use]
#[inline]
pub fn clamp_range(&self, slice: RopeSlice) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thumbs up here. This is a good method to have, regardless of whether we incorporate it into ensure_invariants() or not.

@TylerAldrich
Copy link
Author

This seems like a reasonable place to fix this. But I am a little concerned that clamping like this might end up masking out-of-bounds bugs that should otherwise be fixed.

For example, although I haven't looked closely at all, there's a chance that this change is actually just masking the bug you're running into, which may also manifest in other ways than out-of-bounds.

This is a great point - being unfamiliar with the codebase I'm not exactly sure how many places use Selection/Ranges that may have subtle bugs hidden by this.

I did a little extra digging and from what I can tell, the cause of the panic can manifest in a few ways, but it's due to the view's JumpList containing a jump that is now out-of-bounds. After further experimentation it can happen in quite a few ways - e.g. from the last line of a file, use :goto <n>, saving your previous location in the JumpList. Then, removing that line will cause your next jump to panic due to this out-of-bounds.

It seems like this could feasibly be addressed when returning the Option<&Jump> from JumpList::forward/JumpList::backward (before the call to doc.set_selection, which uses ensure_invariants to fix the out-of-bounds range).

I did however push an update to the comment of ensure_invariants - definitely agree the range clamping should be explicitly mentioned there if it is to remain!

@cessen cessen requested a review from archseer April 14, 2022 01:52
@cessen
Copy link
Contributor

cessen commented Apr 14, 2022

Adding @archseer since I think he had some thoughts about this.

@archseer
Copy link
Member

Yeah so the issue here is that the jumplists aren't updated as documents change, so a selection on the jumplist can become invalid if you delete enough of the document.

The solution would be to also map any external references when calling doc.apply but this will require some thought to properly fix so I think your solution is fine to prevent panics.

Could you also log a warning if clamping happened? I'd also like us to panic in debug mode (potentially with extra checks) so these cases can be caught in tests. kakoune for example has a few methods that do checking in debug:

https://github.com/mawww/kakoune/blob/d758bbf09b949f0fef88ef3c6762cc55098cdb95/src/selection.cc#L267-L297=

https://github.com/mawww/kakoune/blob/d758bbf09b949f0fef88ef3c6762cc55098cdb95/src/buffer.cc#L383-L393=

@the-mikedavis the-mikedavis added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label May 18, 2022
@kirawi kirawi added A-helix-term Area: Helix term improvements S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 13, 2022
@the-mikedavis
Copy link
Member

Fixed in #4186

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-helix-term Area: Helix term improvements S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants