diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-06-24 06:50:48 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2022-06-24 07:26:06 +0800 |
commit | affeb5c6ddbda0c34a9513e393d2b05f622e1514 (patch) | |
tree | 7e6e6e9fb52b131b6fee5c412280353cbddae53e /src/nvim/regexp.c | |
parent | 589f418fceadfbbc10a6d1d37dd5d2ed026342b5 (diff) | |
download | rneovim-affeb5c6ddbda0c34a9513e393d2b05f622e1514.tar.gz rneovim-affeb5c6ddbda0c34a9513e393d2b05f622e1514.tar.bz2 rneovim-affeb5c6ddbda0c34a9513e393d2b05f622e1514.zip |
vim-patch:8.2.5146: memory leak when substitute expression nests
Problem: Memory leak when substitute expression nests.
Solution: Use an array of expression results.
https://github.com/vim/vim/commit/44ddf19ec0ff59c969658ec7d9ed42070c59c51b
Cherry-pick a comment change from patch 8.2.5057.
N/A patches for version.c:
vim-patch:8.2.5154: still mentioning version8, some cosmetic issues
Problem: Still mentioning version8, some cosmetic issues.
Solution: Prefer mentioning version9, cosmetic improvements.
https://github.com/vim/vim/commit/abd56da30bae4a5c6c20b9363ccae12f7b126026
Diffstat (limited to 'src/nvim/regexp.c')
-rw-r--r-- | src/nvim/regexp.c | 64 |
1 files changed, 45 insertions, 19 deletions
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 352f4dfe39..45f2cf0e1d 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -110,6 +110,7 @@ static char_u e_empty_sb[] = N_("E70: Empty %s%%[]"); static char_u e_recursive[] = N_("E956: Cannot use pattern recursively"); static char_u e_regexp_number_after_dot_pos_search[] = N_("E1204: No Number allowed after .: '\\%%%c'"); +static char_u e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep"); #define NOT_MULTI 0 #define MULTI_ONE 1 @@ -1722,6 +1723,19 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de return result; } +// When nesting more than a couple levels it's probably a mistake. +#define MAX_REGSUB_NESTING 4 +static char_u *eval_result[MAX_REGSUB_NESTING] = { NULL, NULL, NULL, NULL }; + +#if defined(EXITFREE) +void free_resub_eval_result(void) +{ + for (int i = 0; i < MAX_REGSUB_NESTING; i++) { + XFREE_CLEAR(eval_result[i]); + } +} +#endif + static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags) { char_u *src; @@ -1734,7 +1748,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des fptr_T func_one = (fptr_T)NULL; linenr_T clnum = 0; // init for GCC int len = 0; // init for GCC - static char_u *eval_result = NULL; + static int nesting = 0; bool copy = flags & REGSUB_COPY; // Be paranoid... @@ -1745,6 +1759,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des if (prog_magic_wrong()) { return 0; } + if (nesting == MAX_REGSUB_NESTING) { + emsg(_(e_substitute_nesting_too_deep)); + return 0; + } + int nested = nesting; src = source; dst = dest; @@ -1752,19 +1771,20 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des if (expr != NULL || (source[0] == '\\' && source[1] == '=')) { // To make sure that the length doesn't change between checking the // length and copying the string, and to speed up things, the - // resulting string is saved from the call with "flags & REGSUB_COPY" - // == 0 to the call with "flags & REGSUB_COPY" != 0. + // resulting string is saved from the call with + // "flags & REGSUB_COPY" == 0 to the call with + // "flags & REGSUB_COPY" != 0. if (copy) { - if (eval_result != NULL) { - STRCPY(dest, eval_result); - dst += STRLEN(eval_result); - XFREE_CLEAR(eval_result); + if (eval_result[nested] != NULL) { + STRCPY(dest, eval_result[nested]); + dst += STRLEN(eval_result[nested]); + XFREE_CLEAR(eval_result[nested]); } } else { const bool prev_can_f_submatch = can_f_submatch; regsubmatch_T rsm_save; - xfree(eval_result); + XFREE_CLEAR(eval_result[nested]); // The expression may contain substitute(), which calls us // recursively. Make sure submatch() gets the text from the first @@ -1779,6 +1799,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des rsm.sm_maxline = rex.reg_maxline; rsm.sm_line_lbr = rex.reg_line_lbr; + // Although unlikely, it is possible that the expression invokes a + // substitute command (it might fail, but still). Therefore keep + // an array of eval results. + nesting++; + if (expr != NULL) { typval_T argv[2]; typval_T rettv; @@ -1806,23 +1831,24 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des } if (rettv.v_type == VAR_UNKNOWN) { // something failed, no need to report another error - eval_result = NULL; + eval_result[nested] = NULL; } else { char buf[NUMBUFLEN]; - eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf); - if (eval_result != NULL) { - eval_result = vim_strsave(eval_result); + eval_result[nested] = (char_u *)tv_get_string_buf_chk(&rettv, buf); + if (eval_result[nested] != NULL) { + eval_result[nested] = vim_strsave(eval_result[nested]); } } tv_clear(&rettv); } else { - eval_result = (char_u *)eval_to_string((char *)source + 2, NULL, true); + eval_result[nested] = (char_u *)eval_to_string((char *)source + 2, NULL, true); } + nesting--; - if (eval_result != NULL) { + if (eval_result[nested] != NULL) { int had_backslash = false; - for (s = eval_result; *s != NUL; MB_PTR_ADV(s)) { + for (s = eval_result[nested]; *s != NUL; MB_PTR_ADV(s)) { // Change NL to CR, so that it becomes a line break, // unless called from vim_regexec_nl(). // Skip over a backslashed character. @@ -1844,12 +1870,12 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des } if (had_backslash && (flags & REGSUB_BACKSLASH)) { // Backslashes will be consumed, need to double them. - s = vim_strsave_escaped(eval_result, (char_u *)"\\"); - xfree(eval_result); - eval_result = s; + s = vim_strsave_escaped(eval_result[nested], (char_u *)"\\"); + xfree(eval_result[nested]); + eval_result[nested] = s; } - dst += STRLEN(eval_result); + dst += STRLEN(eval_result[nested]); } can_f_submatch = prev_can_f_submatch; |