# HG changeset patch # User Bram Moolenaar # Date 1683664204 -7200 # Node ID 3bf4736649b752a8284e533799326ef91a25420e # Parent f62f661344d6f0b2c87f10135ae8805a725b4717 patch 9.0.1532: crash when expanding "~" in substitute causes very long text Commit: https://github.com/vim/vim/commit/ab9a2d884b3a4abe319606ea95a5a6d6b01cd73a Author: Bram Moolenaar Date: Tue May 9 21:15:30 2023 +0100 patch 9.0.1532: crash when expanding "~" in substitute causes very long text Problem: Crash when expanding "~" in substitute causes very long text. Solution: Limit the text length to MAXCOL. diff --git a/src/regexp.c b/src/regexp.c --- a/src/regexp.c +++ b/src/regexp.c @@ -1767,10 +1767,7 @@ do_Lower(int *d, int c) regtilde(char_u *source, int magic) { char_u *newsub = source; - char_u *tmpsub; char_u *p; - int len; - int prevlen; for (p = newsub; *p; ++p) { @@ -1779,24 +1776,35 @@ regtilde(char_u *source, int magic) if (reg_prev_sub != NULL) { // length = len(newsub) - 1 + len(prev_sub) + 1 - prevlen = (int)STRLEN(reg_prev_sub); - tmpsub = alloc(STRLEN(newsub) + prevlen); + // Avoid making the text longer than MAXCOL, it will cause + // trouble at some point. + size_t prevsublen = STRLEN(reg_prev_sub); + size_t newsublen = STRLEN(newsub); + if (prevsublen > MAXCOL || newsublen > MAXCOL + || newsublen + prevsublen > MAXCOL) + { + emsg(_(e_resulting_text_too_long)); + break; + } + + char_u *tmpsub = alloc(newsublen + prevsublen); if (tmpsub != NULL) { // copy prefix - len = (int)(p - newsub); // not including ~ - mch_memmove(tmpsub, newsub, (size_t)len); + size_t prefixlen = p - newsub; // not including ~ + mch_memmove(tmpsub, newsub, prefixlen); // interpret tilde - mch_memmove(tmpsub + len, reg_prev_sub, (size_t)prevlen); + mch_memmove(tmpsub + prefixlen, reg_prev_sub, + prevsublen); // copy postfix if (!magic) ++p; // back off backslash - STRCPY(tmpsub + len + prevlen, p + 1); + STRCPY(tmpsub + prefixlen + prevsublen, p + 1); - if (newsub != source) // already allocated newsub + if (newsub != source) // allocated newsub before vim_free(newsub); newsub = tmpsub; - p = newsub + len + prevlen; + p = newsub + prefixlen + prevsublen; } } else if (magic) diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim --- a/src/testdir/test_substitute.vim +++ b/src/testdir/test_substitute.vim @@ -1414,6 +1414,20 @@ func Test_substitute_short_cmd() bw! endfunc +" Check handling expanding "~" resulting in extremely long text. +func Test_substitute_tilde_too_long() + enew! + + s/.*/ixxx + s//~~~~~~~~~AAAAAAA@( + + " Either fails with "out of memory" or "text too long". + " This can take a long time. + call assert_fails('sil! norm &&&&&&&&&', ['E1240:\|E342:']) + + bwipe! +endfunc + " This should be done last to reveal a memory leak when vim_regsub_both() is " called to evaluate an expression but it is not used in a second call. func Test_z_substitute_expr_leak() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1532, +/**/ 1531, /**/ 1530,