diff options
-rw-r--r-- | src/ex_cmds.c | 50 | ||||
-rw-r--r-- | src/testdir/test_substitute.vim | 48 | ||||
-rw-r--r-- | src/version.c | 2 |
3 files changed, 85 insertions, 15 deletions
diff --git a/src/ex_cmds.c b/src/ex_cmds.c index c5f912e7ee..a08682b071 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -3737,13 +3737,13 @@ ex_substitute(exarg_T *eap) int save_do_all; // remember user specified 'g' flag int save_do_ask; // remember user specified 'c' flag char_u *pat = NULL, *sub = NULL; // init for GCC - char_u *sub_copy = NULL; int delimiter; int sublen; int got_quit = FALSE; int got_match = FALSE; int which_pat; char_u *cmd; + char_u *p; int save_State; linenr_T first_line = 0; // first changed line linenr_T last_line= 0; // below last changed line AFTER the @@ -3827,8 +3827,12 @@ ex_substitute(exarg_T *eap) * Small incompatibility: vi sees '\n' as end of the command, but in * Vim we want to use '\n' to find/substitute a NUL. */ - sub = cmd; // remember the start of the substitution + p = cmd; // remember the start of the substitution cmd = skip_substitute(cmd, delimiter); + sub = vim_strsave(p); + if (sub == NULL) + // out of memory + return; if (!eap->skip) { @@ -3839,14 +3843,22 @@ ex_substitute(exarg_T *eap) if (old_sub == NULL) // there is no previous command { emsg(_(e_no_previous_substitute_regular_expression)); + vim_free(sub); return; } - sub = old_sub; + vim_free(sub); + sub = vim_strsave(old_sub); + if (sub == NULL) + // out of memory + return; } else { vim_free(old_sub); old_sub = vim_strsave(sub); + if (old_sub == NULL) + // out of memory + return; } } } @@ -3858,7 +3870,7 @@ ex_substitute(exarg_T *eap) return; } pat = NULL; // search_regcomp() will use previous pattern - sub = old_sub; + sub = vim_strsave(old_sub); // Vi compatibility quirk: repeating with ":s" keeps the cursor in the // last column after using "$". @@ -3877,7 +3889,10 @@ ex_substitute(exarg_T *eap) linenr_T joined_lines_count; if (eap->skip) + { + vim_free(sub); return; + } curwin->w_cursor.lnum = eap->line1; if (*cmd == 'l') eap->flags = EXFLAG_LIST; @@ -3904,6 +3919,7 @@ ex_substitute(exarg_T *eap) save_re_pat(RE_SUBST, pat, magic_isset()); // put pattern in history add_to_history(HIST_SEARCH, pat, TRUE, NUL); + vim_free(sub); return; } @@ -3991,6 +4007,7 @@ ex_substitute(exarg_T *eap) if (i <= 0 && !eap->skip && subflags.do_error) { emsg(_(e_positive_count_required)); + vim_free(sub); return; } else if (i >= INT_MAX) @@ -3998,6 +4015,7 @@ ex_substitute(exarg_T *eap) char buf[20]; vim_snprintf(buf, sizeof(buf), "%ld", i); semsg(_(e_val_too_large), buf); + vim_free(sub); return; } eap->line1 = eap->line2; @@ -4016,17 +4034,22 @@ ex_substitute(exarg_T *eap) if (eap->nextcmd == NULL) { semsg(_(e_trailing_characters_str), cmd); + vim_free(sub); return; } } if (eap->skip) // not executing commands, only parsing + { + vim_free(sub); return; + } if (!subflags.do_count && !curbuf->b_p_ma) { // Substitution is not allowed in non-'modifiable' buffer emsg(_(e_cannot_make_changes_modifiable_is_off)); + vim_free(sub); return; } @@ -4034,6 +4057,7 @@ ex_substitute(exarg_T *eap) { if (subflags.do_error) emsg(_(e_invalid_command)); + vim_free(sub); return; } @@ -4054,20 +4078,20 @@ ex_substitute(exarg_T *eap) */ if (sub[0] == '\\' && sub[1] == '=') { - sub = vim_strsave(sub); - if (sub == NULL) + p = vim_strsave(sub); + vim_free(sub); + if (p == NULL) return; - sub_copy = sub; + sub = p; } else { - char_u *newsub = regtilde(sub, magic_isset()); + p = regtilde(sub, magic_isset()); - if (newsub != sub) + if (p != sub) { - // newsub was allocated, free it later. - sub_copy = newsub; - sub = newsub; + vim_free(sub); + sub = p; } } @@ -4965,7 +4989,7 @@ outofmem: #endif vim_regfree(regmatch.regprog); - vim_free(sub_copy); + vim_free(sub); // Restore the flag values, they can be used for ":&&". subflags.do_all = save_do_all; diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim index 3ed159799f..7c2bbb4767 100644 --- a/src/testdir/test_substitute.vim +++ b/src/testdir/test_substitute.vim @@ -4,6 +4,32 @@ source shared.vim source check.vim source screendump.vim +" NOTE: This needs to be the first test to be +" run in the file, since it depends on +" that the previous substitution atom +" was not yet set. +" +" recursive call of :s and sub-replace special +" (did cause heap-use-after free in < v9.0.2121) +func Test_aaaa_substitute_expr_recursive_special() + func R() + " FIXME: leaving out the 'n' flag leaks memory, why? + %s/./\='.'/gn + endfunc + new Xfoobar_UAF + put ='abcdef' + let bufnr = bufnr('%') + try + silent! :s/./~\=R()/0 + "call assert_fails(':s/./~\=R()/0', 'E939:') + let @/='.' + ~g + catch /^Vim\%((\a\+)\)\=:E565:/ + endtry + delfunc R + exe bufnr .. "bw!" +endfunc + func Test_multiline_subst() enew! call append(0, ["1 aa", @@ -147,7 +173,6 @@ func Test_substitute_repeat() call feedkeys("Qsc\<CR>y", 'tx') bwipe! endfunc - " Test %s/\n// which is implemented as a special case to use a " more efficient join rather than doing a regular substitution. func Test_substitute_join() @@ -1447,11 +1472,30 @@ func Test_substitute_expr_switch_win() endfunc new Xfoobar let bufnr = bufnr('%') - put ="abcdef" + put ='abcdef' silent! s/\%')/\=R() call assert_fails(':%s/./\=R()/g', 'E565:') delfunc R exe bufnr .. "bw!" endfunc +" recursive call of :s using test-replace special +func Test_substitute_expr_recursive() + func Q() + %s/./\='foobar'/gn + return "foobar" + endfunc + func R() + %s/./\=Q()/g + endfunc + new Xfoobar_UAF + let bufnr = bufnr('%') + put ='abcdef' + silent! s/./\=R()/g + call assert_fails(':%s/./\=R()/g', 'E565:') + delfunc R + delfunc Q + exe bufnr .. "bw!" +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index edf6b576fe..642da01d8b 100644 --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2121, +/**/ 2120, /**/ 2119, |