To: vim_dev@googlegroups.com Subject: Patch 8.2.5046 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.5046 Problem: vim_regsub() can overwrite the destination. Solution: Pass the destination length, give an error when it doesn't fit. Files: src/regexp.h, src/regexp.c, src/proto/regexp.pro, src/eval.c, src/ex_cmds.c *** ../vim-8.2.5045/src/regexp.h 2022-04-23 10:41:31.092696680 +0100 --- src/regexp.h 2022-05-30 19:15:22.277948283 +0100 *************** *** 177,180 **** --- 177,185 ---- //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 *** ../vim-8.2.5045/src/regexp.c 2022-05-05 13:52:59.416192105 +0100 --- src/regexp.c 2022-05-30 19:59:58.326309942 +0100 *************** *** 1649,1655 **** */ 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 fptr_T do_upper(int *d, int c) --- 1649,1655 ---- */ typedef void (*(*fptr_T)(int *, int)); ! static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags); static fptr_T do_upper(int *d, int c) *************** *** 1822,1834 **** * vim_regsub() - perform substitutions after a vim_regexec() or * vim_regexec_multi() match. * ! * If "copy" is TRUE really copy into "dest". ! * If "copy" is FALSE nothing is copied, this is just to find out the length ! * of the result. * ! * If "backslash" is TRUE, 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 * vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back --- 1822,1835 ---- * vim_regsub() - perform substitutions after a vim_regexec() or * vim_regexec_multi() match. * ! * If "flags" has REGSUB_COPY really copy into "dest[destlen]". ! * Oterwise nothing is copied, only compue the length of the result. * ! * If "flags" has REGSUB_MAGIC then behave like 'magic' is set. ! * ! * 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 * vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back *************** *** 1842,1850 **** char_u *source, typval_T *expr, char_u *dest, ! int copy, ! int magic, ! int backslash) { int result; regexec_T rex_save; --- 1843,1850 ---- char_u *source, typval_T *expr, char_u *dest, ! int destlen, ! int flags) { int result; regexec_T rex_save; *************** *** 1860,1866 **** rex.reg_maxline = 0; rex.reg_buf = curbuf; rex.reg_line_lbr = TRUE; ! result = vim_regsub_both(source, expr, dest, copy, magic, backslash); rex_in_use = rex_in_use_save; if (rex_in_use) --- 1860,1866 ---- rex.reg_maxline = 0; rex.reg_buf = curbuf; rex.reg_line_lbr = TRUE; ! result = vim_regsub_both(source, expr, dest, destlen, flags); rex_in_use = rex_in_use_save; if (rex_in_use) *************** *** 1875,1883 **** linenr_T lnum, char_u *source, char_u *dest, ! int copy, ! int magic, ! int backslash) { int result; regexec_T rex_save; --- 1875,1882 ---- linenr_T lnum, char_u *source, char_u *dest, ! int destlen, ! int flags) { int result; regexec_T rex_save; *************** *** 1894,1900 **** rex.reg_firstlnum = lnum; rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum; rex.reg_line_lbr = FALSE; ! result = vim_regsub_both(source, NULL, dest, copy, magic, backslash); rex_in_use = rex_in_use_save; if (rex_in_use) --- 1893,1899 ---- rex.reg_firstlnum = lnum; rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum; rex.reg_line_lbr = FALSE; ! result = vim_regsub_both(source, NULL, dest, destlen, flags); rex_in_use = rex_in_use_save; if (rex_in_use) *************** *** 1908,1916 **** char_u *source, typval_T *expr, char_u *dest, ! int copy, ! int magic, ! int backslash) { char_u *src; char_u *dst; --- 1907,1914 ---- char_u *source, typval_T *expr, char_u *dest, ! int destlen, ! int flags) { char_u *src; char_u *dst; *************** *** 1925,1930 **** --- 1923,1929 ---- #ifdef FEAT_EVAL static char_u *eval_result = NULL; #endif + int copy = flags & REGSUB_COPY; // Be paranoid... if ((source == NULL && expr == NULL) || dest == NULL) *************** *** 1945,1952 **** #ifdef FEAT_EVAL // 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 "copy" == FALSE to the ! // call with "copy" == TRUE. if (copy) { if (eval_result != NULL) --- 1944,1951 ---- #ifdef FEAT_EVAL // 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. if (copy) { if (eval_result != NULL) *************** *** 2054,2060 **** had_backslash = TRUE; } } ! if (had_backslash && backslash) { // Backslashes will be consumed, need to double them. s = vim_strsave_escaped(eval_result, (char_u *)"\\"); --- 2053,2059 ---- had_backslash = TRUE; } } ! if (had_backslash && (flags & REGSUB_BACKSLASH)) { // Backslashes will be consumed, need to double them. s = vim_strsave_escaped(eval_result, (char_u *)"\\"); *************** *** 2077,2087 **** else while ((c = *src++) != NUL) { ! if (c == '&' && magic) no = 0; else if (c == '\\' && *src != NUL) { ! if (*src == '&' && !magic) { ++src; no = 0; --- 2076,2086 ---- else while ((c = *src++) != NUL) { ! if (c == '&' && (flags & REGSUB_MAGIC)) no = 0; else if (c == '\\' && *src != NUL) { ! if (*src == '&' && !(flags & REGSUB_MAGIC)) { ++src; no = 0; *************** *** 2115,2120 **** --- 2114,2124 ---- // Copy a special key as-is. if (copy) { + if (dst + 3 > dest + destlen) + { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } *dst++ = c; *dst++ = *src++; *dst++ = *src++; *************** *** 2141,2150 **** // If "backslash" is TRUE the backslash will be removed // later. Used to insert a literal CR. ! default: if (backslash) { if (copy) *dst = '\\'; ++dst; } c = *src++; --- 2145,2161 ---- // If "backslash" is TRUE the backslash will be removed // later. Used to insert a literal CR. ! default: if (flags & REGSUB_BACKSLASH) { if (copy) + { + if (dst + 1 > dest + destlen) + { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } *dst = '\\'; + } ++dst; } c = *src++; *************** *** 2166,2175 **** if (has_mbyte) { int totlen = mb_ptr2len(src - 1); if (copy) mb_char2bytes(cc, dst); ! dst += mb_char2len(cc) - 1; if (enc_utf8) { int clen = utf_ptr2len(src - 1); --- 2177,2194 ---- if (has_mbyte) { int totlen = mb_ptr2len(src - 1); + int charlen = mb_char2len(cc); if (copy) + { + if (dst + charlen > dest + destlen) + { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } mb_char2bytes(cc, dst); ! } ! dst += charlen - 1; if (enc_utf8) { int clen = utf_ptr2len(src - 1); *************** *** 2179,2193 **** if (clen < totlen) { if (copy) mch_memmove(dst + 1, src - 1 + clen, (size_t)(totlen - clen)); dst += totlen - clen; } } src += totlen - 1; } else if (copy) ! *dst = cc; dst++; } else --- 2198,2226 ---- if (clen < totlen) { if (copy) + { + if (dst + totlen - clen > dest + destlen) + { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } mch_memmove(dst + 1, src - 1 + clen, (size_t)(totlen - clen)); + } dst += totlen - clen; } } src += totlen - 1; } else if (copy) ! { ! if (dst + 1 > dest + destlen) ! { ! iemsg("vim_regsub_both(): not enough space"); ! return 0; ! } ! *dst = cc; ! } dst++; } else *************** *** 2226,2232 **** --- 2259,2272 ---- if (rex.reg_mmatch->endpos[no].lnum == clnum) break; if (copy) + { + if (dst + 1 > dest + destlen) + { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } *dst = CAR; + } ++dst; s = reg_getline(++clnum); if (rex.reg_mmatch->endpos[no].lnum == clnum) *************** *** 2245,2251 **** } else { ! if (backslash && (*s == CAR || *s == '\\')) { /* * Insert a backslash in front of a CR, otherwise --- 2285,2292 ---- } else { ! if ((flags & REGSUB_BACKSLASH) ! && (*s == CAR || *s == '\\')) { /* * Insert a backslash in front of a CR, otherwise *************** *** 2255,2260 **** --- 2296,2306 ---- */ if (copy) { + if (dst + 2 > dest + destlen) + { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } dst[0] = '\\'; dst[1] = *s; } *************** *** 2279,2284 **** --- 2325,2331 ---- if (has_mbyte) { int l; + int charlen; // Copy composing characters separately, one // at a time. *************** *** 2289,2300 **** s += l; len -= l; if (copy) mb_char2bytes(cc, dst); ! dst += mb_char2len(cc) - 1; } else if (copy) ! *dst = cc; dst++; } --- 2336,2362 ---- s += l; len -= l; + charlen = mb_char2len(cc); if (copy) + { + if (dst + charlen > dest + destlen) + { + iemsg("vim_regsub_both(): not enough space"); + return 0; + } mb_char2bytes(cc, dst); ! } ! dst += charlen - 1; } else if (copy) ! { ! if (dst + 1 > dest + destlen) ! { ! iemsg("vim_regsub_both(): not enough space"); ! return 0; ! } ! *dst = cc; ! } dst++; } *************** *** 2711,2717 **** /* * Match a regexp against a string. ! * "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). * Note: "rmp->regprog" may be freed and changed. * Uses curbuf for line count and 'iskeyword'. * When "nl" is TRUE consider a "\n" in "line" to be a line break. --- 2773,2779 ---- /* * Match a regexp against a string. ! * "rmp->regprog" must be a compiled regexp as returned by vim_regcomp(). * Note: "rmp->regprog" may be freed and changed. * Uses curbuf for line count and 'iskeyword'. * When "nl" is TRUE consider a "\n" in "line" to be a line break. *** ../vim-8.2.5045/src/proto/regexp.pro 2021-01-04 11:41:49.507891351 +0000 --- src/proto/regexp.pro 2022-05-30 19:51:14.371762806 +0100 *************** *** 6,13 **** reg_extmatch_T *ref_extmatch(reg_extmatch_T *em); void unref_extmatch(reg_extmatch_T *em); 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_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash); char_u *reg_submatch(int no); list_T *reg_submatch_list(int no); int vim_regcomp_had_eol(void); --- 6,13 ---- reg_extmatch_T *ref_extmatch(reg_extmatch_T *em); void unref_extmatch(reg_extmatch_T *em); char_u *regtilde(char_u *source, int magic); ! 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 destlen, int flags); char_u *reg_submatch(int no); list_T *reg_submatch_list(int no); int vim_regcomp_had_eol(void); *** ../vim-8.2.5045/src/eval.c 2022-05-27 17:26:50.542119974 +0100 --- src/eval.c 2022-05-30 19:48:08.336278016 +0100 *************** *** 6905,6911 **** * - The substituted text. * - The text after the match. */ ! sublen = vim_regsub(®match, sub, expr, tail, FALSE, TRUE, FALSE); if (ga_grow(&ga, (int)((end - tail) + sublen - (regmatch.endp[0] - regmatch.startp[0]))) == FAIL) { --- 6905,6911 ---- * - The substituted text. * - The text after the match. */ ! sublen = vim_regsub(®match, sub, expr, tail, 0, REGSUB_MAGIC); if (ga_grow(&ga, (int)((end - tail) + sublen - (regmatch.endp[0] - regmatch.startp[0]))) == FAIL) { *************** *** 6917,6924 **** i = (int)(regmatch.startp[0] - tail); mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); // add the substituted text ! (void)vim_regsub(®match, sub, expr, (char_u *)ga.ga_data ! + ga.ga_len + i, TRUE, TRUE, FALSE); ga.ga_len += i + sublen - 1; tail = regmatch.endp[0]; if (*tail == NUL) --- 6917,6925 ---- i = (int)(regmatch.startp[0] - tail); mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); // add the substituted text ! (void)vim_regsub(®match, sub, expr, ! (char_u *)ga.ga_data + ga.ga_len + i, sublen, ! REGSUB_COPY | REGSUB_MAGIC); ga.ga_len += i + sublen - 1; tail = regmatch.endp[0]; if (*tail == NUL) *** ../vim-8.2.5045/src/ex_cmds.c 2022-05-27 17:26:50.542119974 +0100 --- src/ex_cmds.c 2022-05-30 19:50:30.599884093 +0100 *************** *** 4419,4425 **** // get length of substitution part sublen = vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, ! sub, sub_firstline, FALSE, magic_isset(), TRUE); #ifdef FEAT_EVAL --textlock; --- 4419,4427 ---- // get length of substitution part sublen = vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, ! sub, sub_firstline, 0, ! REGSUB_BACKSLASH ! | (magic_isset() ? REGSUB_MAGIC : 0)); #ifdef FEAT_EVAL --textlock; *************** *** 4528,4534 **** #endif (void)vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, ! sub, new_end, TRUE, magic_isset(), TRUE); #ifdef FEAT_EVAL --textlock; #endif --- 4530,4538 ---- #endif (void)vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, ! sub, new_end, sublen, ! REGSUB_COPY | REGSUB_BACKSLASH ! | (magic_isset() ? REGSUB_MAGIC : 0)); #ifdef FEAT_EVAL --textlock; #endif *** ../vim-8.2.5045/src/version.c 2022-05-30 17:57:47.098595199 +0100 --- src/version.c 2022-05-30 20:58:02.666114736 +0100 *************** *** 736,737 **** --- 736,739 ---- { /* Add new patch number below this line */ + /**/ + 5046, /**/ -- We do not stumble over mountains, but over molehills. Confucius /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///