changeset 16706:77bcb5055fec v8.1.1355

patch 8.1.1355: obvious mistakes are accepted as valid expressions commit https://github.com/vim/vim/commit/16e9b85113e0b354ece1cb4f5fcc7866850f3685 Author: Bram Moolenaar <Bram@vim.org> Date: Sun May 19 19:59:35 2019 +0200 patch 8.1.1355: obvious mistakes are accepted as valid expressions Problem: Obvious mistakes are accepted as valid expressions. Solution: Be more strict about parsing numbers. (Yasuhiro Matsumoto, closes #3981)
author Bram Moolenaar <Bram@vim.org>
date Sun, 19 May 2019 20:00:09 +0200
parents 033ac0bfd3ba
children 3b3e32ba0c5c
files src/charset.c src/eval.c src/evalfunc.c src/ex_cmds.c src/ex_getln.c src/json.c src/misc2.c src/ops.c src/option.c src/proto/charset.pro src/testdir/test_expr.vim src/testdir/test_json.vim src/version.c
diffstat 13 files changed, 86 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/charset.c
+++ b/src/charset.c
@@ -1776,25 +1776,30 @@ vim_isblankline(char_u *lbuf)
  * If "what" contains STR2NR_HEX recognize hex numbers
  * If "what" contains STR2NR_FORCE always assume bin/oct/hex.
  * If maxlen > 0, check at a maximum maxlen chars.
+ * If strict is TRUE, check the number strictly. return *len = 0 if fail.
  */
     void
 vim_str2nr(
     char_u		*start,
-    int			*prep,	    /* return: type of number 0 = decimal, 'x'
-				       or 'X' is hex, '0' = octal, 'b' or 'B'
-				       is bin */
-    int			*len,	    /* return: detected length of number */
-    int			what,	    /* what numbers to recognize */
-    varnumber_T		*nptr,	    /* return: signed result */
-    uvarnumber_T	*unptr,	    /* return: unsigned result */
-    int			maxlen)     /* max length of string to check */
+    int			*prep,	    // return: type of number 0 = decimal, 'x'
+				    // or 'X' is hex, '0' = octal, 'b' or 'B'
+				    // is bin
+    int			*len,	    // return: detected length of number
+    int			what,	    // what numbers to recognize
+    varnumber_T		*nptr,	    // return: signed result
+    uvarnumber_T	*unptr,	    // return: unsigned result
+    int			maxlen,     // max length of string to check
+    int			strict)     // check strictly
 {
     char_u	    *ptr = start;
-    int		    pre = 0;		/* default is decimal */
+    int		    pre = 0;		// default is decimal
     int		    negative = FALSE;
     uvarnumber_T    un = 0;
     int		    n;
 
+    if (len != NULL)
+	*len = 0;
+
     if (ptr[0] == '-')
     {
 	negative = TRUE;
@@ -1836,9 +1841,7 @@ vim_str2nr(
 	}
     }
 
-    /*
-    * Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
-    */
+    // Do the conversion manually to avoid sscanf() quirks.
     n = 1;
     if (pre == 'B' || pre == 'b' || what == STR2NR_BIN + STR2NR_FORCE)
     {
@@ -1907,6 +1910,10 @@ vim_str2nr(
 		break;
 	}
     }
+    // Check for an alpha-numeric character immediately following, that is
+    // most likely a typo.
+    if (strict && n - 1 != maxlen && ASCII_ISALNUM(*ptr))
+	return;
 
     if (prep != NULL)
 	*prep = pre;
--- a/src/eval.c
+++ b/src/eval.c
@@ -4453,7 +4453,13 @@ eval7(
 		else
 		{
 		    // decimal, hex or octal number
-		    vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0);
+		    vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, TRUE);
+		    if (len == 0)
+		    {
+			semsg(_(e_invexpr2), *arg);
+			ret = FAIL;
+			break;
+		    }
 		    *arg += len;
 		    if (evaluate)
 		    {
@@ -7460,7 +7466,7 @@ tv_get_number_chk(typval_T *varp, int *d
 	case VAR_STRING:
 	    if (varp->vval.v_string != NULL)
 		vim_str2nr(varp->vval.v_string, NULL, NULL,
-						    STR2NR_ALL, &n, NULL, 0);
+					    STR2NR_ALL, &n, NULL, 0, FALSE);
 	    return n;
 	case VAR_LIST:
 	    emsg(_("E745: Using a List as a Number"));
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -13199,7 +13199,8 @@ f_str2nr(typval_T *argvars, typval_T *re
 	case 16: what = STR2NR_HEX + STR2NR_FORCE; break;
 	default: what = 0;
     }
-    vim_str2nr(p, NULL, NULL, what, &n, NULL, 0);
+    vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE);
+    // Text after the number is silently ignored.
     if (isneg)
 	rettv->vval.v_number = -n;
     else
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -558,7 +558,8 @@ ex_sort(exarg_T *eap)
 		{
 		    nrs[lnum - eap->line1].st_u.num.is_number = TRUE;
 		    vim_str2nr(s, NULL, NULL, sort_what,
-			       &nrs[lnum - eap->line1].st_u.num.value, NULL, 0);
+			&nrs[lnum - eap->line1].st_u.num.value,
+			NULL, 0, FALSE);
 		}
 	    }
 #ifdef FEAT_FLOAT
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -6470,7 +6470,7 @@ get_list_range(char_u **str, int *num1, 
     *str = skipwhite(*str);
     if (**str == '-' || vim_isdigit(**str))  /* parse "from" part of range */
     {
-	vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0);
+	vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, FALSE);
 	*str += len;
 	*num1 = (int)num;
 	first = TRUE;
@@ -6479,7 +6479,7 @@ get_list_range(char_u **str, int *num1, 
     if (**str == ',')			/* parse "to" part of range */
     {
 	*str = skipwhite(*str + 1);
-	vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0);
+	vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, FALSE);
 	if (len > 0)
 	{
 	    *num2 = (int)num;
--- a/src/json.c
+++ b/src/json.c
@@ -452,7 +452,12 @@ json_decode_string(js_read_T *reader, ty
 		    nr = 0;
 		    len = 0;
 		    vim_str2nr(p + 2, NULL, &len,
-				     STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
+			     STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4, TRUE);
+		    if (len == 0)
+		    {
+			ga_clear(&ga);
+			return FAIL;
+		    }
 		    p += len + 2;
 		    if (0xd800 <= nr && nr <= 0xdfff
 			    && (int)(reader->js_end - p) >= 6
@@ -463,7 +468,12 @@ json_decode_string(js_read_T *reader, ty
 			/* decode surrogate pair: \ud812\u3456 */
 			len = 0;
 			vim_str2nr(p + 2, NULL, &len,
-				     STR2NR_HEX + STR2NR_FORCE, &nr2, NULL, 4);
+			     STR2NR_HEX + STR2NR_FORCE, &nr2, NULL, 4, TRUE);
+			if (len == 0)
+			{
+			    ga_clear(&ga);
+			    return FAIL;
+			}
 			if (0xdc00 <= nr2 && nr2 <= 0xdfff)
 			{
 			    p += len + 2;
@@ -783,7 +793,13 @@ json_decode_item(js_read_T *reader, typv
 
 			    vim_str2nr(reader->js_buf + reader->js_used,
 				    NULL, &len, 0, /* what */
-				    &nr, NULL, 0);
+				    &nr, NULL, 0, TRUE);
+			    if (len == 0)
+			    {
+				emsg(_(e_invarg));
+				retval = FAIL;
+				goto theend;
+			    }
 			    if (cur_item != NULL)
 			    {
 				cur_item->v_type = VAR_NUMBER;
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -2832,7 +2832,12 @@ find_special_key(
 	    bp += 3;	/* skip t_xx, xx may be '-' or '>' */
 	else if (STRNICMP(bp, "char-", 5) == 0)
 	{
-	    vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0);
+	    vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, TRUE);
+	    if (l == 0)
+	    {
+		emsg(_(e_invarg));
+		return 0;
+	    }
 	    bp += l + 5;
 	    break;
 	}
@@ -2864,7 +2869,12 @@ find_special_key(
 						 && VIM_ISDIGIT(last_dash[6]))
 	    {
 		/* <Char-123> or <Char-033> or <Char-0x33> */
-		vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0);
+		vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, TRUE);
+		if (l == 0)
+		{
+		    emsg(_(e_invarg));
+		    return 0;
+		}
 		key = (int)n;
 	    }
 	    else
--- a/src/ops.c
+++ b/src/ops.c
@@ -5794,7 +5794,7 @@ do_addsub(
 		0 + (dobin ? STR2NR_BIN : 0)
 		    + (dooct ? STR2NR_OCT : 0)
 		    + (dohex ? STR2NR_HEX : 0),
-		NULL, &n, maxlen);
+		NULL, &n, maxlen, FALSE);
 
 	/* ignore leading '-' for hex and octal and bin numbers */
 	if (pre && negative)
--- a/src/option.c
+++ b/src/option.c
@@ -4762,10 +4762,10 @@ do_set(
 			    /* Allow negative (for 'undolevels'), octal and
 			     * hex numbers. */
 			    vim_str2nr(arg, NULL, &i, STR2NR_ALL,
-							     &value, NULL, 0);
-			    if (arg[i] != NUL && !VIM_ISWHITE(arg[i]))
+						     &value, NULL, 0, TRUE);
+			    if (i == 0 || (arg[i] != NUL && !VIM_ISWHITE(arg[i])))
 			    {
-				errmsg = e_invarg;
+				errmsg = N_("E521: Number required after =");
 				goto skip;
 			    }
 			}
--- a/src/proto/charset.pro
+++ b/src/proto/charset.pro
@@ -54,7 +54,7 @@ char_u *skiptowhite(char_u *p);
 char_u *skiptowhite_esc(char_u *p);
 long getdigits(char_u **pp);
 int vim_isblankline(char_u *lbuf);
-void vim_str2nr(char_u *start, int *prep, int *len, int what, varnumber_T *nptr, uvarnumber_T *unptr, int maxlen);
+void vim_str2nr(char_u *start, int *prep, int *len, int what, varnumber_T *nptr, uvarnumber_T *unptr, int maxlen, int strict);
 int hex2nr(int c);
 int hexhex2nr(char_u *p);
 int rem_backslash(char_u *str);
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -512,3 +512,14 @@ func Test_empty_concatenate()
   call assert_equal('b', 'a'[4:0] . 'b')
   call assert_equal('b', 'b' . 'a'[4:0])
 endfunc
+
+func Test_broken_number()
+  let X = 'bad'
+  call assert_fails('echo 1X', 'E15:')
+  call assert_fails('echo 0b1X', 'E15:')
+  call assert_fails('echo 0b12', 'E15:')
+  call assert_fails('echo 0x1X', 'E15:')
+  call assert_fails('echo 011X', 'E15:')
+  call assert_equal(2, str2nr('2a'))
+  call assert_fails('inoremap <Char-0b1z> b', 'E474:')
+endfunc
--- a/src/testdir/test_json.vim
+++ b/src/testdir/test_json.vim
@@ -176,6 +176,10 @@ func Test_json_decode()
 
   call assert_fails('call json_decode("{{}:42}")', "E474:")
   call assert_fails('call json_decode("{[]:42}")', "E474:")
+
+  call assert_fails('call json_decode("\"\\u111Z\"")', 'E474:')
+  call assert_equal('[😂]', json_decode('"[\uD83D\uDE02]"'))
+  call assert_equal('a😂b', json_decode('"a\uD83D\uDE02b"'))
 endfunc
 
 let s:jsl5 = '[7,,,]'
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1355,
+/**/
     1354,
 /**/
     1353,