changeset 34561:0adcad161c46 v9.1.0181

patch 9.1.0181: no overflow check for string formatting Commit: https://github.com/vim/vim/commit/c35fc03dbd47582b256776fb11f11d8ceb24f8f0 Author: Christ van Willegen <cvwillegen@gmail.com> Date: Thu Mar 14 18:30:41 2024 +0100 patch 9.1.0181: no overflow check for string formatting Problem: no overflow check for string formatting Solution: Check message formatting function for overflow. (Chris van Willegen) closes: #13799 Signed-off-by: Christ van Willegen <cvwillegen@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 14 Mar 2024 19:15:03 +0100
parents 8b94adf4474a
children 4a9250f5cf09
files runtime/doc/builtin.txt src/strings.c src/testdir/test_format.vim src/version.c
diffstat 4 files changed, 239 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -1,4 +1,4 @@
-*builtin.txt*	For Vim version 9.1.  Last change: 2024 Mar 13
+*builtin.txt*	For Vim version 9.1.  Last change: 2024 Mar 14
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -7105,6 +7105,9 @@ printf({fmt}, {expr1} ...)				*printf()*
 		    echo printf("%1$*2$.*3$f", 1.4142135, 6, 2)
 <		      1.41
 
+		You will get an overflow error |E1510|, when the field-width
+		or precision will result in a string longer than 6400 chars.
+
 							*E1500*
 		You cannot mix positional and non-positional arguments: >
 		    echo printf("%s%1$s", "One", "Two")
--- a/src/strings.c
+++ b/src/strings.c
@@ -2468,6 +2468,55 @@ adjust_types(
     return OK;
 }
 
+    static void
+format_overflow_error(const char *pstart)
+{
+    size_t	arglen = 0;
+    char	*argcopy = NULL;
+    const char	*p = pstart;
+
+    while (VIM_ISDIGIT((int)(*p)))
+	++p;
+
+    arglen = p - pstart;
+    argcopy = ALLOC_CLEAR_MULT(char, arglen + 1);
+    if (argcopy != NULL)
+    {
+	strncpy(argcopy, pstart, arglen);
+	semsg(_( e_val_too_large), argcopy);
+	free(argcopy);
+    }
+    else
+	semsg(_(e_out_of_memory_allocating_nr_bytes), arglen);
+}
+
+#define MAX_ALLOWED_STRING_WIDTH 6400
+
+    static int
+get_unsigned_int(
+    const char *pstart,
+    const char **p,
+    unsigned int *uj)
+{
+    *uj = **p - '0';
+    ++*p;
+
+    while (VIM_ISDIGIT((int)(**p)) && *uj < MAX_ALLOWED_STRING_WIDTH)
+    {
+	*uj = 10 * *uj + (unsigned int)(**p - '0');
+	++*p;
+    }
+
+    if (*uj > MAX_ALLOWED_STRING_WIDTH)
+    {
+	format_overflow_error(pstart);
+	return FAIL;
+    }
+
+    return OK;
+}
+
+
     static int
 parse_fmt_types(
     const char  ***ap_types,
@@ -2511,6 +2560,7 @@ parse_fmt_types(
 	    // variable for positional arg
 	    int		pos_arg = -1;
 	    const char	*ptype = NULL;
+	    const char	*pstart = p+1;
 
 	    p++;  // skip '%'
 
@@ -2531,10 +2581,11 @@ parse_fmt_types(
 		}
 
 		// Positional argument
-		unsigned int uj = *p++ - '0';
-
-		while (VIM_ISDIGIT((int)(*p)))
-		    uj = 10 * uj + (unsigned int)(*p++ - '0');
+		unsigned int uj;
+
+		if (get_unsigned_int(pstart, &p, &uj) == FAIL)
+		    goto error;
+
 		pos_arg = uj;
 
 		any_pos = 1;
@@ -2571,10 +2622,10 @@ parse_fmt_types(
 		if (VIM_ISDIGIT((int)(*p)))
 		{
 		    // Positional argument field width
-		    unsigned int uj = *p++ - '0';
-
-		    while (VIM_ISDIGIT((int)(*p)))
-			uj = 10 * uj + (unsigned int)(*p++ - '0');
+		    unsigned int uj;
+
+		    if (get_unsigned_int(arg + 1, &p, &uj) == FAIL)
+			goto error;
 
 		    if (*p != '$')
 		    {
@@ -2601,10 +2652,11 @@ parse_fmt_types(
 	    {
 		// size_t could be wider than unsigned int; make sure we treat
 		// argument like common implementations do
-		unsigned int uj = *p++ - '0';
-
-		while (VIM_ISDIGIT((int)(*p)))
-		    uj = 10 * uj + (unsigned int)(*p++ - '0');
+		const char *digstart = p;
+		unsigned int uj;
+
+		if (get_unsigned_int(digstart, &p, &uj) == FAIL)
+		    goto error;
 
 		if (*p == '$')
 		{
@@ -2625,10 +2677,10 @@ parse_fmt_types(
 		    if (VIM_ISDIGIT((int)(*p)))
 		    {
 			// Parse precision
-			unsigned int uj = *p++ - '0';
-
-			while (VIM_ISDIGIT((int)(*p)))
-			    uj = 10 * uj + (unsigned int)(*p++ - '0');
+			unsigned int uj;
+
+			if (get_unsigned_int(arg + 1, &p, &uj) == FAIL)
+			    goto error;
 
 			if (*p == '$')
 			{
@@ -2656,10 +2708,11 @@ parse_fmt_types(
 		{
 		    // size_t could be wider than unsigned int; make sure we
 		    // treat argument like common implementations do
-		    unsigned int uj = *p++ - '0';
-
-		    while (VIM_ISDIGIT((int)(*p)))
-			uj = 10 * uj + (unsigned int)(*p++ - '0');
+		    const char *digstart = p;
+		    unsigned int uj;
+
+		    if (get_unsigned_int(digstart, &p, &uj) == FAIL)
+			goto error;
 
 		    if (*p == '$')
 		    {
@@ -2968,10 +3021,12 @@ vim_vsnprintf_typval(
 	    if (*ptype == '$')
 	    {
 		// Positional argument
-		unsigned int uj = *p++ - '0';
-
-		while (VIM_ISDIGIT((int)(*p)))
-		    uj = 10 * uj + (unsigned int)(*p++ - '0');
+		const char *digstart = p;
+		unsigned int uj;
+
+		if (get_unsigned_int(digstart, &p, &uj) == FAIL)
+		    goto error;
+
 		pos_arg = uj;
 
 		++p;
@@ -3002,16 +3057,18 @@ vim_vsnprintf_typval(
 	    if (*p == '*')
 	    {
 		int j;
+		const char *digstart = p + 1;
 
 		p++;
 
 		if (VIM_ISDIGIT((int)(*p)))
 		{
 		    // Positional argument field width
-		    unsigned int uj = *p++ - '0';
-
-		    while (VIM_ISDIGIT((int)(*p)))
-			uj = 10 * uj + (unsigned int)(*p++ - '0');
+		    unsigned int uj;
+
+		    if (get_unsigned_int(digstart, &p, &uj) == FAIL)
+			goto error;
+
 		    arg_idx = uj;
 
 		    ++p;
@@ -3025,6 +3082,12 @@ vim_vsnprintf_typval(
 				     &arg_cur, fmt),
 			va_arg(ap, int));
 
+		if (j > MAX_ALLOWED_STRING_WIDTH)
+		{
+		    format_overflow_error(digstart);
+		    goto error;
+		}
+
 		if (j >= 0)
 		    min_field_width = j;
 		else
@@ -3037,10 +3100,18 @@ vim_vsnprintf_typval(
 	    {
 		// size_t could be wider than unsigned int; make sure we treat
 		// argument like common implementations do
-		unsigned int uj = *p++ - '0';
-
-		while (VIM_ISDIGIT((int)(*p)))
-		    uj = 10 * uj + (unsigned int)(*p++ - '0');
+		const char *digstart = p;
+		unsigned int uj;
+
+		if (get_unsigned_int(digstart, &p, &uj) == FAIL)
+		    goto error;
+
+		if (uj > MAX_ALLOWED_STRING_WIDTH)
+		{
+		    format_overflow_error(digstart);
+		    goto error;
+		}
+
 		min_field_width = uj;
 	    }
 
@@ -3054,25 +3125,35 @@ vim_vsnprintf_typval(
 		{
 		    // size_t could be wider than unsigned int; make sure we
 		    // treat argument like common implementations do
-		    unsigned int uj = *p++ - '0';
-
-		    while (VIM_ISDIGIT((int)(*p)))
-			uj = 10 * uj + (unsigned int)(*p++ - '0');
+		    const char *digstart = p;
+		    unsigned int uj;
+
+		    if (get_unsigned_int(digstart, &p, &uj) == FAIL)
+			goto error;
+
+		    if (uj > MAX_ALLOWED_STRING_WIDTH)
+		    {
+			format_overflow_error(digstart);
+			goto error;
+		    }
+
 		    precision = uj;
 		}
 		else if (*p == '*')
 		{
 		    int j;
+		    const char *digstart = p;
 
 		    p++;
 
 		    if (VIM_ISDIGIT((int)(*p)))
 		    {
 			// positional argument
-			unsigned int uj = *p++ - '0';
-
-			while (VIM_ISDIGIT((int)(*p)))
-			    uj = 10 * uj + (unsigned int)(*p++ - '0');
+			unsigned int uj;
+
+			if (get_unsigned_int(digstart, &p, &uj) == FAIL)
+			    goto error;
+
 			arg_idx = uj;
 
 			++p;
@@ -3086,6 +3167,12 @@ vim_vsnprintf_typval(
 					 &arg_cur, fmt),
 			    va_arg(ap, int));
 
+		    if (j > MAX_ALLOWED_STRING_WIDTH)
+		    {
+			format_overflow_error(digstart);
+			goto error;
+		    }
+
 		    if (j >= 0)
 			precision = j;
 		    else
@@ -3873,6 +3960,7 @@ vim_vsnprintf_typval(
     if (tvs != NULL && tvs[num_posarg != 0 ? num_posarg : arg_idx - 1].v_type != VAR_UNKNOWN)
 	emsg(_(e_too_many_arguments_to_printf));
 
+error:
     vim_free((char*)ap_types);
     va_end(ap);
 
--- a/src/testdir/test_format.vim
+++ b/src/testdir/test_format.vim
@@ -105,67 +105,6 @@ func Test_printf_pos_misc()
   END
   call v9.CheckLegacyAndVim9Success(lines)
 
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 1, 3, 4)"], "E767:")
-
-  call v9.CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d%d', 1, 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$*1$d', 1, 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%2$.*1$d%d', 1, 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$.*1$d', 1, 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$%')"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$')"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$_')"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*d', 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*2$d', 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*d', 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%*.*1$d', 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*d', 3)"], "E1500:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*1$d', 3)"], "E1500:")
-
-  call v9.CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1501:")
-
-  call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d %1$ld', 3, 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$*1$d', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %2$*1$d', 3, 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1502:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1502:")
-
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1503:")
-
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$s', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ud %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$f', 3.0)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$d %1$ld', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$d', 3)"], "E1504:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$d', 3)"], "E1504:")
-
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$.2$d', 3)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%01$d', 3)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%01$0d', 3)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3.*2$d', 3)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.2$d', 3)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:")
-  call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:")
 endfunc
 
 func Test_printf_pos_float()
@@ -287,8 +226,6 @@ func Test_printf_pos_float()
       call assert_equal('nan', printf('%1$S', -0.0 / 0.0))
   END
   call v9.CheckLegacyAndVim9Success(lines)
-
-  call v9.CheckLegacyAndVim9Failure(['echo printf("%f", "a")'], 'E807:')
 endfunc
 
 func Test_printf_pos_errors()
@@ -299,6 +236,111 @@ func Test_printf_pos_errors()
   call v9.CheckLegacyAndVim9Failure(['echo printf("%1$s")'], 'E1503:')
   call v9.CheckLegacyAndVim9Failure(['echo printf("%1$d", 1.2)'], 'E805:')
   call v9.CheckLegacyAndVim9Failure(['echo printf("%1$f")'], 'E1503:')
+
+  call v9.CheckLegacyAndVim9Failure(['echo printf("%f", "a")'], 'E807:')
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 1, 3, 4)"], "E767:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d%d', 1, 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$*1$d', 1, 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%2$.*1$d%d', 1, 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$.*1$d', 1, 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$%')"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$')"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$_')"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*d', 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*2$d', 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*d', 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%*.*1$d', 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*d', 3)"], "E1500:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*1$d', 3)"], "E1500:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1501:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d %1$ld', 3, 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$*1$d', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %2$*1$d', 3, 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1502:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1502:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1503:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$s', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ud %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$f', 3.0)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$d %1$ld', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$d', 3)"], "E1504:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$d', 3)"], "E1504:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$.2$d', 3)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%01$d', 3)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%01$0d', 3)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3.*2$d', 3)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.2$d', 3)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%.123456789$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%.123456789d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789d', 5)"], "E1510:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.5d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.5d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.123456789d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$987654321.5d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.987654321d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.987654321d', 5)"], "E1510:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.5d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.5d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.123456789d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*987654321$.5d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.987654321d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.987654321d', 5)"], "E1510:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.*1$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.*1$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.*123456789$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$987654321.*1$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.*987654321$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.*987654321$d', 5)"], "E1510:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.*1$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.*1$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*123456789d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*987654321$.*1$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.*987654321$d', 5)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.*987654321$d', 5)"], "E1510:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2$.*1$d', 5, 9999)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*2$d', 5, 9999)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%2$*3$.*1$d', 5, 9123, 9321)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2$.*3$d', 5, 9123, 9321)"], "E1510:")
+  call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$.*3$d', 5, 9123, 9312)"], "E1510:")
+
+  call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2$d', 5, 9999)"], "E1510:")
 endfunc
 
 func Test_printf_pos_64bit()
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    181,
+/**/
     180,
 /**/
     179,