#1350·xi-editor

[xi-core-lib] Arithmetic overflow occurs when using APIs

Author: HeeillWangCreated Sep 22, 2023Updated Jul 27, 2024

Details

I found some overflow patterns on xi-core-lib public APIs. All of these cases triggered when external crates use API of xi-core-lib. As overflow looks quite obvious, I just list up for reproduce code for the first case. Please let me know if anyone need reproduce code for other cases.

Case-1

Thread '<unnamed>' panicked at 'attempt to subtract with overflow', xi-core-lib-0.3.0/src/selection.rs:156
    /// Deletes all the regions that intersect or (if delete_adjacent = true) touch the given range.
    pub fn delete_range(&mut self, start: usize, end: usize, delete_adjacent: bool) {
        let mut first = self.search(start);
        let mut last = self.search(end);
        if first >= self.regions.len() {
            return;
        }
        if !delete_adjacent && self.regions[first].max() == start {
            first += 1;
        }
        if last < self.regions.len()
            && ((delete_adjacent && self.regions[last].min() <= end)
                || (!delete_adjacent && self.regions[last].min() < end))
        {
            last += 1;
        }
        remove_n_at(&mut self.regions, first, last - first);  // overflow!
    }

reproduce with :

let mut s  = Selection::new_simple(SelRegion::new(0, 0));
s.delete_range(0, 0, false);

Case-2

Thread '<unnamed>' panicked at 'attempt to subtract with overflow', xi-core-lib-0.3.0/src/line_cache_shadow.rs:316
    pub fn request_lines(&mut self, start: usize, end: usize) {
        let mut spans: Vec<(usize, RenderTactic)> = Vec::new();
        let mut i = 0;
        let mut line_num = 0;
        while i < self.spans.len() {
            let span = &self.spans[i];
            if line_num + span.0 <= start {
                spans.push(*span);
                line_num += span.0;
                i += 1;
            } else {
                if line_num < start {
                    spans.push((start - line_num, span.1));
                }
                break;
            }
        }
        spans.push((end - start, RenderTactic::Render));  //overflow!

Case-3

Thread '<unnamed>' panicked at 'attempt to add with overflow', xi-core-lib-0.3.0/src/line_cache_shadow.rs:287
    pub fn create(total_height: usize, first_line: usize, height: usize) -> RenderPlan {
        let mut spans = Vec::new();
        let mut last = 0;
        let first_line = min(first_line, total_height);
        if first_line > PRESERVE_EXTENT {
            last = first_line - PRESERVE_EXTENT;
            spans.push((last, RenderTactic::Discard));
        }
        if first_line > SCROLL_SLOP {
            let n = first_line - SCROLL_SLOP - last;
            spans.push((n, RenderTactic::Preserve));
            last += n;
        }
        let render_end = min(first_line + height + SCROLL_SLOP, total_height);
        spans.push((render_end - last, RenderTactic::Render));
        last = render_end;
        let preserve_end = min(first_line + height + PRESERVE_EXTENT, total_height);  // overflow!

Expected vs Actual

Overflow should be prevented with checked operations. or we may consider adding assert statement.