changeset 9898:bff8a09016a5 v7.4.2223

commit https://github.com/vim/vim/commit/d3c907b5d2b352482b580a0cf687cbbea4c19ea1 Author: Bram Moolenaar <Bram@vim.org> 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.
author Christian Brabandt <cb@256bit.org>
date Wed, 17 Aug 2016 21:45:07 +0200
parents 9d1354639a36
children 816999bfd4c8
files src/Makefile src/evalfunc.c src/getchar.c src/macros.h src/os_unix.c src/os_win32.c src/spell.c src/testdir/test_alot_utf8.vim src/testdir/test_regexp_utf8.vim src/testdir/test_source_utf8.vim src/version.c
diffstat 11 files changed, 67 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- 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 \
--- 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;
 	    }
 	}
--- 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;
--- 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
--- 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)
--- 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)
--- 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
--- 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
--- 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
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
--- 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,