0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 8.2.5046: vim_regsub() can overwrite the destination

Problem:    vim_regsub() can overwrite the destination.
Solution:   Pass the destination length, give an error when it doesn't fit.
This commit is contained in:
Bram Moolenaar
2022-05-30 20:58:55 +01:00
parent 10db31f949
commit 4aaf3e7f4d
6 changed files with 111 additions and 37 deletions

View File

@@ -6905,7 +6905,7 @@ do_string_sub(
* - The substituted text. * - The substituted text.
* - The text after the match. * - The text after the match.
*/ */
sublen = vim_regsub(&regmatch, sub, expr, tail, FALSE, TRUE, FALSE); sublen = vim_regsub(&regmatch, sub, expr, tail, 0, REGSUB_MAGIC);
if (ga_grow(&ga, (int)((end - tail) + sublen - if (ga_grow(&ga, (int)((end - tail) + sublen -
(regmatch.endp[0] - regmatch.startp[0]))) == FAIL) (regmatch.endp[0] - regmatch.startp[0]))) == FAIL)
{ {
@@ -6917,8 +6917,9 @@ do_string_sub(
i = (int)(regmatch.startp[0] - tail); i = (int)(regmatch.startp[0] - tail);
mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
// add the substituted text // add the substituted text
(void)vim_regsub(&regmatch, sub, expr, (char_u *)ga.ga_data (void)vim_regsub(&regmatch, 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 = regmatch.endp[0]; tail = regmatch.endp[0];
if (*tail == NUL) if (*tail == NUL)

View File

@@ -4419,7 +4419,9 @@ ex_substitute(exarg_T *eap)
// get length of substitution part // get length of substitution part
sublen = vim_regsub_multi(&regmatch, sublen = vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum, sub_firstlnum - regmatch.startpos[0].lnum,
sub, sub_firstline, FALSE, magic_isset(), TRUE); sub, sub_firstline, 0,
REGSUB_BACKSLASH
| (magic_isset() ? REGSUB_MAGIC : 0));
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
--textlock; --textlock;
@@ -4528,7 +4530,9 @@ ex_substitute(exarg_T *eap)
#endif #endif
(void)vim_regsub_multi(&regmatch, (void)vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum, sub_firstlnum - regmatch.startpos[0].lnum,
sub, new_end, TRUE, magic_isset(), TRUE); sub, new_end, sublen,
REGSUB_COPY | REGSUB_BACKSLASH
| (magic_isset() ? REGSUB_MAGIC : 0));
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
--textlock; --textlock;
#endif #endif

View File

@@ -6,8 +6,8 @@ char_u *skip_regexp_ex(char_u *startp, int dirc, int magic, char_u **newp, int *
reg_extmatch_T *ref_extmatch(reg_extmatch_T *em); reg_extmatch_T *ref_extmatch(reg_extmatch_T *em);
void unref_extmatch(reg_extmatch_T *em); void unref_extmatch(reg_extmatch_T *em);
char_u *regtilde(char_u *source, int magic); char_u *regtilde(char_u *source, int magic);
int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int copy, int magic, int backslash); int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int destlen, int flags);
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash); int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int destlen, int flags);
char_u *reg_submatch(int no); char_u *reg_submatch(int no);
list_T *reg_submatch_list(int no); list_T *reg_submatch_list(int no);
int vim_regcomp_had_eol(void); int vim_regcomp_had_eol(void);

View File

@@ -1649,7 +1649,7 @@ cstrchr(char_u *s, int c)
*/ */
typedef void (*(*fptr_T)(int *, int)); typedef void (*(*fptr_T)(int *, int));
static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int copy, int magic, int backslash); static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags);
static fptr_T static fptr_T
do_upper(int *d, int c) do_upper(int *d, int c)
@@ -1822,13 +1822,14 @@ 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
@@ -1842,9 +1843,8 @@ vim_regsub(
char_u *source, char_u *source,
typval_T *expr, typval_T *expr,
char_u *dest, char_u *dest,
int copy, int destlen,
int magic, int flags)
int backslash)
{ {
int result; int result;
regexec_T rex_save; regexec_T rex_save;
@@ -1860,7 +1860,7 @@ vim_regsub(
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;
result = vim_regsub_both(source, expr, dest, copy, magic, backslash); 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)
@@ -1875,9 +1875,8 @@ vim_regsub_multi(
linenr_T lnum, linenr_T lnum,
char_u *source, char_u *source,
char_u *dest, char_u *dest,
int copy, int destlen,
int magic, int flags)
int backslash)
{ {
int result; int result;
regexec_T rex_save; regexec_T rex_save;
@@ -1894,7 +1893,7 @@ vim_regsub_multi(
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;
result = vim_regsub_both(source, NULL, dest, copy, magic, backslash); 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)
@@ -1908,9 +1907,8 @@ vim_regsub_both(
char_u *source, char_u *source,
typval_T *expr, typval_T *expr,
char_u *dest, char_u *dest,
int copy, int destlen,
int magic, int flags)
int backslash)
{ {
char_u *src; char_u *src;
char_u *dst; char_u *dst;
@@ -1925,6 +1923,7 @@ vim_regsub_both(
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
static char_u *eval_result = NULL; static char_u *eval_result = NULL;
#endif #endif
int copy = flags & REGSUB_COPY;
// Be paranoid... // Be paranoid...
if ((source == NULL && expr == NULL) || dest == NULL) if ((source == NULL && expr == NULL) || dest == NULL)
@@ -1945,8 +1944,8 @@ vim_regsub_both(
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
// 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)
@@ -2054,7 +2053,7 @@ vim_regsub_both(
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 *)"\\");
@@ -2077,11 +2076,11 @@ vim_regsub_both(
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;
@@ -2115,6 +2114,11 @@ vim_regsub_both(
// 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++;
@@ -2141,10 +2145,17 @@ vim_regsub_both(
// 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: if (backslash) default: if (flags & REGSUB_BACKSLASH)
{ {
if (copy) if (copy)
{
if (dst + 1 > dest + destlen)
{
iemsg("vim_regsub_both(): not enough space");
return 0;
}
*dst = '\\'; *dst = '\\';
}
++dst; ++dst;
} }
c = *src++; c = *src++;
@@ -2166,10 +2177,18 @@ vim_regsub_both(
if (has_mbyte) if (has_mbyte)
{ {
int totlen = mb_ptr2len(src - 1); int totlen = mb_ptr2len(src - 1);
int charlen = mb_char2len(cc);
if (copy) if (copy)
{
if (dst + charlen > dest + destlen)
{
iemsg("vim_regsub_both(): not enough space");
return 0;
}
mb_char2bytes(cc, dst); mb_char2bytes(cc, dst);
dst += mb_char2len(cc) - 1; }
dst += charlen - 1;
if (enc_utf8) if (enc_utf8)
{ {
int clen = utf_ptr2len(src - 1); int clen = utf_ptr2len(src - 1);
@@ -2179,15 +2198,29 @@ vim_regsub_both(
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;
}
mch_memmove(dst + 1, src - 1 + clen, mch_memmove(dst + 1, src - 1 + clen,
(size_t)(totlen - clen)); (size_t)(totlen - clen));
}
dst += totlen - clen; dst += totlen - clen;
} }
} }
src += totlen - 1; src += totlen - 1;
} }
else if (copy) else if (copy)
*dst = cc; {
if (dst + 1 > dest + destlen)
{
iemsg("vim_regsub_both(): not enough space");
return 0;
}
*dst = cc;
}
dst++; dst++;
} }
else else
@@ -2226,7 +2259,14 @@ vim_regsub_both(
if (rex.reg_mmatch->endpos[no].lnum == clnum) if (rex.reg_mmatch->endpos[no].lnum == clnum)
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;
s = reg_getline(++clnum); s = reg_getline(++clnum);
if (rex.reg_mmatch->endpos[no].lnum == clnum) if (rex.reg_mmatch->endpos[no].lnum == clnum)
@@ -2245,7 +2285,8 @@ vim_regsub_both(
} }
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
@@ -2255,6 +2296,11 @@ vim_regsub_both(
*/ */
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;
} }
@@ -2279,6 +2325,7 @@ vim_regsub_both(
if (has_mbyte) if (has_mbyte)
{ {
int l; int l;
int charlen;
// Copy composing characters separately, one // Copy composing characters separately, one
// at a time. // at a time.
@@ -2289,12 +2336,27 @@ vim_regsub_both(
s += l; s += l;
len -= l; len -= l;
charlen = mb_char2len(cc);
if (copy) if (copy)
{
if (dst + charlen > dest + destlen)
{
iemsg("vim_regsub_both(): not enough space");
return 0;
}
mb_char2bytes(cc, dst); mb_char2bytes(cc, dst);
dst += mb_char2len(cc) - 1; }
dst += charlen - 1;
} }
else if (copy) else if (copy)
*dst = cc; {
if (dst + 1 > dest + destlen)
{
iemsg("vim_regsub_both(): not enough space");
return 0;
}
*dst = cc;
}
dst++; dst++;
} }
@@ -2711,7 +2773,7 @@ regprog_in_use(regprog_T *prog)
/* /*
* Match 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.

View File

@@ -177,4 +177,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 // _REGEXP_H #endif // _REGEXP_H

View File

@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
5046,
/**/ /**/
5045, 5045,
/**/ /**/