# HG changeset patch # User Christian Brabandt # Date 1471463107 -7200 # Node ID bff8a09016a59fb92e543653d8c1eb9c5f4f16a3 # Parent 9d1354639a36c08ff360537b0b09f82086b14635 commit https://github.com/vim/vim/commit/d3c907b5d2b352482b580a0cf687cbbea4c19ea1 Author: Bram Moolenaar Date: Wed Aug 17 21:32:09 2016 +0200 patch 7.4.2223 Problem: Buffer overflow when using latin1 character with feedkeys(). Solution: Check for an illegal character. Add a test. diff --git a/src/Makefile b/src/Makefile --- a/src/Makefile +++ b/src/Makefile @@ -2114,6 +2114,7 @@ test_arglist \ test_set \ test_signs \ test_sort \ + test_source_utf8 \ test_startup \ test_startup_utf8 \ test_stat \ diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -11166,7 +11166,7 @@ f_strgetchar(typval_T *argvars, typval_T break; } --charidx; - byteidx += mb_cptr2len(str + byteidx); + byteidx += MB_CPTR2LEN(str + byteidx); } } #else @@ -11326,7 +11326,7 @@ f_strcharpart(typval_T *argvars, typval_ if (nchar > 0) while (nchar > 0 && nbyte < slen) { - nbyte += mb_cptr2len(p + nbyte); + nbyte += MB_CPTR2LEN(p + nbyte); --nchar; } else @@ -11341,7 +11341,7 @@ f_strcharpart(typval_T *argvars, typval_ if (off < 0) len += 1; else - len += mb_cptr2len(p + off); + len += MB_CPTR2LEN(p + off); --charlen; } } diff --git a/src/getchar.c b/src/getchar.c --- a/src/getchar.c +++ b/src/getchar.c @@ -4658,8 +4658,16 @@ vim_strsave_escape_csi( char_u *res; char_u *s, *d; - /* Need a buffer to hold up to three times as much. */ - res = alloc((unsigned)(STRLEN(p) * 3) + 1); + /* Need a buffer to hold up to three times as much. Four in case of an + * illegal utf-8 byte: + * 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER */ + res = alloc((unsigned)(STRLEN(p) * +#ifdef FEAT_MBYTE + 4 +#else + 3 +#endif + ) + 1); if (res != NULL) { d = res; @@ -4674,22 +4682,10 @@ vim_strsave_escape_csi( } else { -#ifdef FEAT_MBYTE - int len = mb_char2len(PTR2CHAR(s)); - int len2 = mb_ptr2len(s); -#endif /* Add character, possibly multi-byte to destination, escaping - * CSI and K_SPECIAL. */ + * CSI and K_SPECIAL. Be careful, it can be an illegal byte! */ d = add_char2buf(PTR2CHAR(s), d); -#ifdef FEAT_MBYTE - while (len < len2) - { - /* add following combining char */ - d = add_char2buf(PTR2CHAR(s + len), d); - len += mb_char2len(PTR2CHAR(s + len)); - } -#endif - mb_ptr_adv(s); + s += MB_CPTR2LEN(s); } } *d = NUL; diff --git a/src/macros.h b/src/macros.h --- a/src/macros.h +++ b/src/macros.h @@ -274,7 +274,7 @@ /* Backup multi-byte pointer. Only use with "p" > "s" ! */ # define mb_ptr_back(s, p) p -= has_mbyte ? ((*mb_head_off)(s, p - 1) + 1) : 1 /* get length of multi-byte char, not including composing chars */ -# define mb_cptr2len(p) (enc_utf8 ? utf_ptr2len(p) : (*mb_ptr2len)(p)) +# define MB_CPTR2LEN(p) (enc_utf8 ? utf_ptr2len(p) : (*mb_ptr2len)(p)) # define MB_COPY_CHAR(f, t) if (has_mbyte) mb_copy_char(&f, &t); else *t++ = *f++ # define MB_CHARLEN(p) (has_mbyte ? mb_charlen(p) : (int)STRLEN(p)) @@ -282,6 +282,7 @@ # define PTR2CHAR(p) (has_mbyte ? mb_ptr2char(p) : (int)*(p)) #else # define MB_PTR2LEN(p) 1 +# define MB_CPTR2LEN(p) 1 # define mb_ptr_adv(p) ++p # define mb_cptr_adv(p) ++p # define mb_ptr_back(s, p) --p diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4806,7 +4806,7 @@ mch_call_shell( * round. */ for (p = buffer; p < buffer + len; p += l) { - l = mb_cptr2len(p); + l = MB_CPTR2LEN(p); if (l == 0) l = 1; /* NUL byte? */ else if (MB_BYTE2LEN(*p) != l) diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -4370,7 +4370,7 @@ dump_pipe(int options, * round. */ for (p = buffer; p < buffer + len; p += l) { - l = mb_cptr2len(p); + l = MB_CPTR2LEN(p); if (l == 0) l = 1; /* NUL byte? */ else if (MB_BYTE2LEN(*p) != l) diff --git a/src/spell.c b/src/spell.c --- a/src/spell.c +++ b/src/spell.c @@ -5379,7 +5379,7 @@ suggest_trie_walk( #ifdef FEAT_MBYTE if (has_mbyte) { - n = mb_cptr2len(p); + n = MB_CPTR2LEN(p); c = mb_ptr2char(p); if (p[n] == NUL) c2 = NUL; @@ -5477,9 +5477,9 @@ suggest_trie_walk( #ifdef FEAT_MBYTE if (has_mbyte) { - n = mb_cptr2len(p); + n = MB_CPTR2LEN(p); c = mb_ptr2char(p); - fl = mb_cptr2len(p + n); + fl = MB_CPTR2LEN(p + n); c2 = mb_ptr2char(p + n); if (!soundfold && !spell_iswordp(p + n + fl, curwin)) c3 = c; /* don't swap non-word char */ @@ -5596,10 +5596,10 @@ suggest_trie_walk( #ifdef FEAT_MBYTE if (has_mbyte) { - n = mb_cptr2len(p); + n = MB_CPTR2LEN(p); c = mb_ptr2char(p); - fl = mb_cptr2len(p + n); - fl += mb_cptr2len(p + n + fl); + fl = MB_CPTR2LEN(p + n); + fl += MB_CPTR2LEN(p + n + fl); mch_memmove(p, p + n, fl); mb_char2bytes(c, p + fl); stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; @@ -5661,10 +5661,10 @@ suggest_trie_walk( #ifdef FEAT_MBYTE if (has_mbyte) { - n = mb_cptr2len(p); - n += mb_cptr2len(p + n); + n = MB_CPTR2LEN(p); + n += MB_CPTR2LEN(p + n); c = mb_ptr2char(p + n); - tl = mb_cptr2len(p + n); + tl = MB_CPTR2LEN(p + n); mch_memmove(p + tl, p, n); mb_char2bytes(c, p); stack[depth].ts_fidxtry = sp->ts_fidx + n + tl; @@ -5955,8 +5955,8 @@ find_keepcap_word(slang_T *slang, char_u #ifdef FEAT_MBYTE if (has_mbyte) { - flen = mb_cptr2len(fword + fwordidx[depth]); - ulen = mb_cptr2len(uword + uwordidx[depth]); + flen = MB_CPTR2LEN(fword + fwordidx[depth]); + ulen = MB_CPTR2LEN(uword + uwordidx[depth]); } else #endif diff --git a/src/testdir/test_alot_utf8.vim b/src/testdir/test_alot_utf8.vim --- a/src/testdir/test_alot_utf8.vim +++ b/src/testdir/test_alot_utf8.vim @@ -8,3 +8,4 @@ source test_expr_utf8.vim source test_matchadd_conceal_utf8.vim source test_regexp_utf8.vim +source test_source_utf8.vim diff --git a/src/testdir/test_regexp_utf8.vim b/src/testdir/test_regexp_utf8.vim --- a/src/testdir/test_regexp_utf8.vim +++ b/src/testdir/test_regexp_utf8.vim @@ -92,18 +92,3 @@ func Test_classes_re2() call s:classes_test() set re=0 endfunc - -func Test_source_utf8() - " check that sourcing a script with 0x80 as second byte works - new - call setline(1, [':%s/àx/--à1234--/g', ':%s/Àx/--À1234--/g']) - write! Xscript - bwipe! - new - call setline(1, [' àx ', ' Àx ']) - source! Xscript | echo - call assert_equal(' --à1234-- ', getline(1)) - call assert_equal(' --À1234-- ', getline(2)) - bwipe! - call delete('Xscript') -endfunc diff --git a/src/testdir/test_source_utf8.vim b/src/testdir/test_source_utf8.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_source_utf8.vim @@ -0,0 +1,33 @@ +" Test the :source! command +if !has('multi_byte') + finish +endif + +func Test_source_utf8() + " check that sourcing a script with 0x80 as second byte works + new + call setline(1, [':%s/àx/--à1234--/g', ':%s/Àx/--À1234--/g']) + write! Xscript + bwipe! + new + call setline(1, [' àx ', ' Àx ']) + source! Xscript | echo + call assert_equal(' --à1234-- ', getline(1)) + call assert_equal(' --À1234-- ', getline(2)) + bwipe! + call delete('Xscript') +endfunc + +func Test_source_latin() + " check that sourcing a latin1 script with a 0xc0 byte works + new + call setline(1, ["call feedkeys('r')", "call feedkeys('\xc0', 'xt')"]) + write! Xscript + bwipe! + new + call setline(1, ['xxx']) + source Xscript + call assert_equal("\u00c0xx", getline(1)) + bwipe! + call delete('Xscript') +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2223, +/**/ 2222, /**/ 2221,