mirror of
https://github.com/neovim/neovim.git
synced 2025-09-29 06:28:35 +00:00
vim-patch:8.2.5046: vim_regsub() can overwrite the destination (#18812)
Problem: vim_regsub() can overwrite the destination.
Solution: Pass the destination length, give an error when it doesn't fit.
4aaf3e7f4d
This commit is contained in:
@@ -10413,7 +10413,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
|
|||||||
// - The text up to where the match is.
|
// - The text up to where the match is.
|
||||||
// - The substituted text.
|
// - The substituted text.
|
||||||
// - The text after the match.
|
// - The text after the match.
|
||||||
sublen = vim_regsub(®match, (char_u *)sub, expr, (char_u *)tail, false, true, false);
|
sublen = vim_regsub(®match, (char_u *)sub, expr, (char_u *)tail, 0, REGSUB_MAGIC);
|
||||||
ga_grow(&ga, (int)((end - tail) + sublen -
|
ga_grow(&ga, (int)((end - tail) + sublen -
|
||||||
(regmatch.endp[0] - regmatch.startp[0])));
|
(regmatch.endp[0] - regmatch.startp[0])));
|
||||||
|
|
||||||
@@ -10421,8 +10421,9 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
|
|||||||
int i = (int)(regmatch.startp[0] - (char_u *)tail);
|
int i = (int)(regmatch.startp[0] - (char_u *)tail);
|
||||||
memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
|
memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
|
||||||
// add the substituted text
|
// add the substituted text
|
||||||
(void)vim_regsub(®match, (char_u *)sub, expr, (char_u *)ga.ga_data
|
(void)vim_regsub(®match, (char_u *)sub, expr,
|
||||||
+ ga.ga_len + i, true, true, false);
|
(char_u *)ga.ga_data + ga.ga_len + i, sublen,
|
||||||
|
REGSUB_COPY | REGSUB_MAGIC);
|
||||||
ga.ga_len += i + sublen - 1;
|
ga.ga_len += i + sublen - 1;
|
||||||
tail = (char *)regmatch.endp[0];
|
tail = (char *)regmatch.endp[0];
|
||||||
if (*tail == NUL) {
|
if (*tail == NUL) {
|
||||||
|
@@ -4078,7 +4078,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
|
|||||||
// get length of substitution part
|
// get length of substitution part
|
||||||
sublen = vim_regsub_multi(®match,
|
sublen = vim_regsub_multi(®match,
|
||||||
sub_firstlnum - regmatch.startpos[0].lnum,
|
sub_firstlnum - regmatch.startpos[0].lnum,
|
||||||
(char_u *)sub, (char_u *)sub_firstline, false, p_magic, true);
|
(char_u *)sub, (char_u *)sub_firstline, 0,
|
||||||
|
REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
|
||||||
// If getting the substitute string caused an error, don't do
|
// If getting the substitute string caused an error, don't do
|
||||||
// the replacement.
|
// the replacement.
|
||||||
// Don't keep flags set by a recursive call
|
// Don't keep flags set by a recursive call
|
||||||
@@ -4118,7 +4119,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
|
|||||||
|
|
||||||
(void)vim_regsub_multi(®match,
|
(void)vim_regsub_multi(®match,
|
||||||
sub_firstlnum - regmatch.startpos[0].lnum,
|
sub_firstlnum - regmatch.startpos[0].lnum,
|
||||||
(char_u *)sub, (char_u *)new_end, true, p_magic, true);
|
(char_u *)sub, (char_u *)new_end, sublen,
|
||||||
|
REGSUB_COPY | REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
|
||||||
sub_nsubs++;
|
sub_nsubs++;
|
||||||
did_sub = true;
|
did_sub = true;
|
||||||
|
|
||||||
|
@@ -1653,21 +1653,22 @@ static void clear_submatch_list(staticList10_T *sl)
|
|||||||
/// vim_regsub() - perform substitutions after a vim_regexec() or
|
/// vim_regsub() - perform substitutions after a vim_regexec() or
|
||||||
/// vim_regexec_multi() match.
|
/// vim_regexec_multi() match.
|
||||||
///
|
///
|
||||||
/// If "copy" is true really copy into "dest".
|
/// If "flags" has REGSUB_COPY really copy into "dest[destlen]".
|
||||||
/// If "copy" is false nothing is copied, this is just to find out the length
|
/// Oterwise nothing is copied, only compue the length of the result.
|
||||||
/// of the result.
|
|
||||||
///
|
///
|
||||||
/// If "backslash" is true, a backslash will be removed later, need to double
|
/// If "flags" has REGSUB_MAGIC then behave like 'magic' is set.
|
||||||
/// them to keep them, and insert a backslash before a CR to avoid it being
|
///
|
||||||
/// replaced with a line break later.
|
/// If "flags" has REGSUB_BACKSLASH a backslash will be removed later, need to
|
||||||
|
/// double them to keep them, and insert a backslash before a CR to avoid it
|
||||||
|
/// being replaced with a line break later.
|
||||||
///
|
///
|
||||||
/// Note: The matched text must not change between the call of
|
/// Note: The matched text must not change between the call of
|
||||||
/// vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
|
/// vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
|
||||||
/// references invalid!
|
/// references invalid!
|
||||||
///
|
///
|
||||||
/// Returns the size of the replacement, including terminating NUL.
|
/// Returns the size of the replacement, including terminating NUL.
|
||||||
int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int copy, int magic,
|
int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int destlen,
|
||||||
int backslash)
|
int flags)
|
||||||
{
|
{
|
||||||
regexec_T rex_save;
|
regexec_T rex_save;
|
||||||
bool rex_in_use_save = rex_in_use;
|
bool rex_in_use_save = rex_in_use;
|
||||||
@@ -1683,7 +1684,7 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in
|
|||||||
rex.reg_maxline = 0;
|
rex.reg_maxline = 0;
|
||||||
rex.reg_buf = curbuf;
|
rex.reg_buf = curbuf;
|
||||||
rex.reg_line_lbr = true;
|
rex.reg_line_lbr = true;
|
||||||
int result = vim_regsub_both(source, expr, dest, copy, magic, backslash);
|
int result = vim_regsub_both(source, expr, dest, destlen, flags);
|
||||||
|
|
||||||
rex_in_use = rex_in_use_save;
|
rex_in_use = rex_in_use_save;
|
||||||
if (rex_in_use) {
|
if (rex_in_use) {
|
||||||
@@ -1693,8 +1694,8 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy,
|
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int destlen,
|
||||||
int magic, int backslash)
|
int flags)
|
||||||
{
|
{
|
||||||
regexec_T rex_save;
|
regexec_T rex_save;
|
||||||
bool rex_in_use_save = rex_in_use;
|
bool rex_in_use_save = rex_in_use;
|
||||||
@@ -1711,7 +1712,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
|
|||||||
rex.reg_firstlnum = lnum;
|
rex.reg_firstlnum = lnum;
|
||||||
rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum;
|
rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum;
|
||||||
rex.reg_line_lbr = false;
|
rex.reg_line_lbr = false;
|
||||||
int result = vim_regsub_both(source, NULL, dest, copy, magic, backslash);
|
int result = vim_regsub_both(source, NULL, dest, destlen, flags);
|
||||||
|
|
||||||
rex_in_use = rex_in_use_save;
|
rex_in_use = rex_in_use_save;
|
||||||
if (rex_in_use) {
|
if (rex_in_use) {
|
||||||
@@ -1721,8 +1722,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int copy, int magic,
|
static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags)
|
||||||
int backslash)
|
|
||||||
{
|
{
|
||||||
char_u *src;
|
char_u *src;
|
||||||
char_u *dst;
|
char_u *dst;
|
||||||
@@ -1735,6 +1735,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
linenr_T clnum = 0; // init for GCC
|
linenr_T clnum = 0; // init for GCC
|
||||||
int len = 0; // init for GCC
|
int len = 0; // init for GCC
|
||||||
static char_u *eval_result = NULL;
|
static char_u *eval_result = NULL;
|
||||||
|
bool copy = flags & REGSUB_COPY;
|
||||||
|
|
||||||
// We need to keep track of how many backslashes we escape, so that the byte
|
// We need to keep track of how many backslashes we escape, so that the byte
|
||||||
// counts for `extmark_splice` are correct.
|
// counts for `extmark_splice` are correct.
|
||||||
@@ -1755,8 +1756,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
if (expr != NULL || (source[0] == '\\' && source[1] == '=')) {
|
if (expr != NULL || (source[0] == '\\' && source[1] == '=')) {
|
||||||
// To make sure that the length doesn't change between checking the
|
// To make sure that the length doesn't change between checking the
|
||||||
// length and copying the string, and to speed up things, the
|
// length and copying the string, and to speed up things, the
|
||||||
// resulting string is saved from the call with "copy" == false to the
|
// resulting string is saved from the call with "flags & REGSUB_COPY"
|
||||||
// call with "copy" == true.
|
// == 0 to the call with "flags & REGSUB_COPY" != 0.
|
||||||
if (copy) {
|
if (copy) {
|
||||||
if (eval_result != NULL) {
|
if (eval_result != NULL) {
|
||||||
STRCPY(dest, eval_result);
|
STRCPY(dest, eval_result);
|
||||||
@@ -1845,7 +1846,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
had_backslash = true;
|
had_backslash = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (had_backslash && backslash) {
|
if (had_backslash && (flags & REGSUB_BACKSLASH)) {
|
||||||
// Backslashes will be consumed, need to double them.
|
// Backslashes will be consumed, need to double them.
|
||||||
s = vim_strsave_escaped(eval_result, (char_u *)"\\");
|
s = vim_strsave_escaped(eval_result, (char_u *)"\\");
|
||||||
xfree(eval_result);
|
xfree(eval_result);
|
||||||
@@ -1862,11 +1863,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while ((c = *src++) != NUL) {
|
while ((c = *src++) != NUL) {
|
||||||
if (c == '&' && magic) {
|
if (c == '&' && (flags & REGSUB_MAGIC)) {
|
||||||
no = 0;
|
no = 0;
|
||||||
} else if (c == '\\' && *src != NUL) {
|
} else if (c == '\\' && *src != NUL) {
|
||||||
if (*src == '&' && !magic) {
|
if (*src == '&' && !(flags & REGSUB_MAGIC)) {
|
||||||
++src;
|
src++;
|
||||||
no = 0;
|
no = 0;
|
||||||
} else if ('0' <= *src && *src <= '9') {
|
} else if ('0' <= *src && *src <= '9') {
|
||||||
no = *src++ - '0';
|
no = *src++ - '0';
|
||||||
@@ -1895,6 +1896,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
if (c == K_SPECIAL && src[0] != NUL && src[1] != NUL) {
|
if (c == K_SPECIAL && src[0] != NUL && src[1] != NUL) {
|
||||||
// Copy a special key as-is.
|
// Copy a special key as-is.
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
if (dst + 3 > dest + destlen) {
|
||||||
|
iemsg("vim_regsub_both(): not enough space");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
*dst++ = c;
|
*dst++ = c;
|
||||||
*dst++ = *src++;
|
*dst++ = *src++;
|
||||||
*dst++ = *src++;
|
*dst++ = *src++;
|
||||||
@@ -1922,9 +1927,13 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
// If "backslash" is true the backslash will be removed
|
// If "backslash" is true the backslash will be removed
|
||||||
// later. Used to insert a literal CR.
|
// later. Used to insert a literal CR.
|
||||||
default:
|
default:
|
||||||
if (backslash) {
|
if (flags & REGSUB_BACKSLASH) {
|
||||||
num_escaped += 1;
|
num_escaped += 1;
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
if (dst + 1 > dest + destlen) {
|
||||||
|
iemsg("vim_regsub_both(): not enough space");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
*dst = '\\';
|
*dst = '\\';
|
||||||
}
|
}
|
||||||
dst++;
|
dst++;
|
||||||
@@ -1945,17 +1954,26 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
}
|
}
|
||||||
|
|
||||||
int totlen = utfc_ptr2len((char *)src - 1);
|
int totlen = utfc_ptr2len((char *)src - 1);
|
||||||
|
int charlen = utf_char2len(cc);
|
||||||
|
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
if (dst + charlen > dest + destlen) {
|
||||||
|
iemsg("vim_regsub_both(): not enough space");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
utf_char2bytes(cc, (char *)dst);
|
utf_char2bytes(cc, (char *)dst);
|
||||||
}
|
}
|
||||||
dst += utf_char2len(cc) - 1;
|
dst += charlen - 1;
|
||||||
int clen = utf_ptr2len((char *)src - 1);
|
int clen = utf_ptr2len((char *)src - 1);
|
||||||
|
|
||||||
// If the character length is shorter than "totlen", there
|
// If the character length is shorter than "totlen", there
|
||||||
// are composing characters; copy them as-is.
|
// are composing characters; copy them as-is.
|
||||||
if (clen < totlen) {
|
if (clen < totlen) {
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
if (dst + totlen - clen > dest + destlen) {
|
||||||
|
iemsg("vim_regsub_both(): not enough space");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
memmove(dst + 1, src - 1 + clen, (size_t)(totlen - clen));
|
memmove(dst + 1, src - 1 + clen, (size_t)(totlen - clen));
|
||||||
}
|
}
|
||||||
dst += totlen - clen;
|
dst += totlen - clen;
|
||||||
@@ -1992,6 +2010,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
if (dst + 1 > dest + destlen) {
|
||||||
|
iemsg("vim_regsub_both(): not enough space");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
*dst = CAR;
|
*dst = CAR;
|
||||||
}
|
}
|
||||||
dst++;
|
dst++;
|
||||||
@@ -2010,14 +2032,16 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
}
|
}
|
||||||
goto exit;
|
goto exit;
|
||||||
} else {
|
} else {
|
||||||
if (backslash && (*s == CAR || *s == '\\')) {
|
if ((flags & REGSUB_BACKSLASH) && (*s == CAR || *s == '\\')) {
|
||||||
/*
|
// Insert a backslash in front of a CR, otherwise
|
||||||
* Insert a backslash in front of a CR, otherwise
|
// it will be replaced by a line break.
|
||||||
* it will be replaced by a line break.
|
// Number of backslashes will be halved later,
|
||||||
* Number of backslashes will be halved later,
|
// double them here.
|
||||||
* double them here.
|
|
||||||
*/
|
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
if (dst + 2 > dest + destlen) {
|
||||||
|
iemsg("vim_regsub_both(): not enough space");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
dst[0] = '\\';
|
dst[0] = '\\';
|
||||||
dst[1] = *s;
|
dst[1] = *s;
|
||||||
}
|
}
|
||||||
@@ -2037,6 +2061,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
|
|
||||||
{
|
{
|
||||||
int l;
|
int l;
|
||||||
|
int charlen;
|
||||||
|
|
||||||
// Copy composing characters separately, one
|
// Copy composing characters separately, one
|
||||||
// at a time.
|
// at a time.
|
||||||
@@ -2044,10 +2069,15 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
|
|||||||
|
|
||||||
s += l;
|
s += l;
|
||||||
len -= l;
|
len -= l;
|
||||||
|
charlen = utf_char2len(cc);
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
if (dst + charlen > dest + destlen) {
|
||||||
|
iemsg("vim_regsub_both(): not enough space");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
utf_char2bytes(cc, (char *)dst);
|
utf_char2bytes(cc, (char *)dst);
|
||||||
}
|
}
|
||||||
dst += utf_char2len(cc) - 1;
|
dst += charlen - 1;
|
||||||
}
|
}
|
||||||
dst++;
|
dst++;
|
||||||
}
|
}
|
||||||
@@ -2386,8 +2416,8 @@ static void report_re_switch(char_u *pat)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Matches a regexp against a string.
|
/// Match a regexp against a string.
|
||||||
/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
|
/// "rmp->regprog" must be a compiled regexp as returned by vim_regcomp().
|
||||||
/// Note: "rmp->regprog" may be freed and changed.
|
/// Note: "rmp->regprog" may be freed and changed.
|
||||||
/// Uses curbuf for line count and 'iskeyword'.
|
/// Uses curbuf for line count and 'iskeyword'.
|
||||||
/// When "nl" is true consider a "\n" in "line" to be a line break.
|
/// When "nl" is true consider a "\n" in "line" to be a line break.
|
||||||
|
@@ -168,4 +168,9 @@ struct regengine {
|
|||||||
// char_u *expr;
|
// char_u *expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Flags used by vim_regsub() and vim_regsub_both()
|
||||||
|
#define REGSUB_COPY 1
|
||||||
|
#define REGSUB_MAGIC 2
|
||||||
|
#define REGSUB_BACKSLASH 4
|
||||||
|
|
||||||
#endif // NVIM_REGEXP_DEFS_H
|
#endif // NVIM_REGEXP_DEFS_H
|
||||||
|
Reference in New Issue
Block a user