From 80f5a009f647aacd492c6e1e7a5f450156cabe13 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 23 Jul 2019 19:24:10 +0100 Subject: [PATCH] Bounds-check terminal selection when clearing scrollback. term_clrsb() was emptying the tree234 of scrollback, without checking whether term->selstart, term->selend and term->selanchor were pointing at places in the now-removed scrollback. If they were, then a subsequent extend-selection operation could give rise to the dreaded 'line==NULL' assertion box. Thanks to the user who sent in one of those debugging dumps, that finally enabled us to track down (at least one case of) this long- standing but extremely rare crash! --- terminal.c | 23 +++++++++++++++++++++++ terminal.h | 5 +++++ 2 files changed, 28 insertions(+) diff --git a/terminal.c b/terminal.c index c1a12c04..a0596b8d 100644 --- a/terminal.c +++ b/terminal.c @@ -1600,6 +1600,24 @@ void term_reconfig(Terminal *term, Conf *conf) term_copy_stuff_from_conf(term); } +/* + * Ensure the position variables describing the ends of the terminal + * selection are in bounds with respect to the actual extent of the + * screen and scrollback. + */ +static void term_selection_bounds_check(Terminal *term) +{ + pos lo, hi; + lo.y = -count234(term->scrollback); + lo.x = 0; + hi.y = count234(term->screen); + hi.x = term->cols - 1; + + term->selstart = bound_pos(term->selstart, lo, hi); + term->selend = bound_pos(term->selend, lo, hi); + term->selanchor = bound_pos(term->selanchor, lo, hi); +} + /* * Clear the scrollback. */ @@ -1621,6 +1639,11 @@ void term_clrsb(Terminal *term) sfree(line); /* this is compressed data, not a termline */ } + /* + * Make sure we didn't invalidate selstart and selend in the process. + */ + term_selection_bounds_check(term); + /* * When clearing the scrollback, we also truncate any termlines on * the current screen which have remembered data from a previous diff --git a/terminal.h b/terminal.h index 1ed0b17d..8da3b9b4 100644 --- a/terminal.h +++ b/terminal.h @@ -475,4 +475,9 @@ static inline bool decpos_fn(pos *p, int cols) #define incpos(p) incpos_fn(&(p), GET_TERM_COLS) #define decpos(p) decpos_fn(&(p), GET_TERM_COLS) +static inline pos bound_pos(pos p, pos lo, pos hi) +{ + return poslt(p, lo) ? lo : poslt(p, hi) ? p : hi; +} + #endif