CVE-2016-1248: Improper Input Validation (CWE-20)
value: vim before patch 8.0.0056 does not properly validate values for the 'filetype', 'syntax' and 'keymap' options, which may result in the execution of arbitrary code if a file with a specially crafted modeline is opened.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 7.8
Published: 2016-11-23T15:59Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/option.c, src/testdir/test_options.vim, src/version.c
Commit date: 2016-11-04 15:23:45 +0100
Commit message: patch 8.0.0056
Problem:    When setting 'filetype' there is no check for a valid name.
Solution:   Only allow valid characters in 'filetype', 'syntax' and 'keymap'.

Diff: diff --git a/src/option.c b/src/option.c
index ebf443b84..8eea1f876 100644
--- a/src/option.c
+++ b/src/option.c
@@ -5820,10 +5820,25 @@ set_string_option(
 #endif
     }
     return r;
 }
 
+/*
+ * Return TRUE if "val" is a valid 'filetype' name.
+ * Also used for 'syntax' and 'keymap'.
+ */
+    static int
+valid_filetype(char_u *val)
+{
+    char_u *s;
+
+    for (s = val; *s != NUL; ++s)
+	if (!ASCII_ISALNUM(*s) && vim_strchr((char_u *)".-_", *s) == NULL)
+	    return FALSE;
+    return TRUE;
+}
+
 /*
  * Handle string options that need some action to perform when changed.
  * Returns NULL for success, or an error message for an error.
  */
     static char_u *
@@ -6233,12 +6248,15 @@ did_set_string_option(
 #endif
 
 #ifdef FEAT_KEYMAP
     else if (varp == &curbuf->b_p_keymap)
     {
-	/* load or unload key mapping tables */
-	errmsg = keymap_init();
+	if (!valid_filetype(*varp))
+	    errmsg = e_invarg;
+	else
+	    /* load or unload key mapping tables */
+	    errmsg = keymap_init();
 
 	if (errmsg == NULL)
 	{
 	    if (*curbuf->b_p_keymap != NUL)
 	    {
@@ -7220,10 +7238,26 @@ did_set_string_option(
 	if (!gui_mch_set_rendering_options(p_rop))
 	    errmsg = e_invarg;
     }
 #endif
 
+#ifdef FEAT_AUTOCMD
+    else if (gvarp == &p_ft)
+    {
+	if (!valid_filetype(*varp))
+	    errmsg = e_invarg;
+    }
+#endif
+
+#ifdef FEAT_SYN_HL
+    else if (gvarp == &p_syn)
+    {
+	if (!valid_filetype(*varp))
+	    errmsg = e_invarg;
+    }
+#endif
+
     /* Options that are a list of flags. */
     else
     {
 	p = NULL;
 	if (varp == &p_ww)
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index 21dd7fe4f..dee435c38 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -46,5 +46,54 @@ func Test_signcolumn()
     set signcolumn=no
     call assert_fails('set signcolumn=nope')
   endif
 endfunc
 
+func Test_filetype_valid()
+  set ft=valid_name
+  call assert_equal("valid_name", &filetype)
+  set ft=valid-name
+  call assert_equal("valid-name", &filetype)
+
+  call assert_fails(":set ft=wrong;name", "E474:")
+  call assert_fails(":set ft=wrong\\\\name", "E474:")
+  call assert_fails(":set ft=wrong\\|name", "E474:")
+  call assert_fails(":set ft=wrong/name", "E474:")
+  call assert_fails(":set ft=wrong\\\nname", "E474:")
+  call assert_equal("valid-name", &filetype)
+
+  exe "set ft=trunc\x00name"
+  call assert_equal("trunc", &filetype)
+endfunc
+
+func Test_syntax_valid()
+  set syn=valid_name
+  call assert_equal("valid_name", &syntax)
+  set syn=valid-name
+  call assert_equal("valid-name", &syntax)
+
+  call assert_fails(":set syn=wrong;name", "E474:")
+  call assert_fails(":set syn=wrong\\\\name", "E474:")
+  call assert_fails(":set syn=wrong\\|name", "E474:")
+  call assert_fails(":set syn=wrong/name", "E474:")
+  call assert_fails(":set syn=wrong\\\nname", "E474:")
+  call assert_equal("valid-name", &syntax)
+
+  exe "set syn=trunc\x00name"
+  call assert_equal("trunc", &syntax)
+endfunc
+
+func Test_keymap_valid()
+  call assert_fails(":set kmp=valid_name", "E544:")
+  call assert_fails(":set kmp=valid_name", "valid_name")
+  call assert_fails(":set kmp=valid-name", "E544:")
+  call assert_fails(":set kmp=valid-name", "valid-name")
+
+  call assert_fails(":set kmp=wrong;name", "E474:")
+  call assert_fails(":set kmp=wrong\\\\name", "E474:")
+  call assert_fails(":set kmp=wrong\\|name", "E474:")
+  call assert_fails(":set kmp=wrong/name", "E474:")
+  call assert_fails(":set kmp=wrong\\\nname", "E474:")
+
+  call assert_fails(":set kmp=trunc\x00name", "E544:")
+  call assert_fails(":set kmp=trunc\x00name", "trunc")
+endfunc
diff --git a/src/version.c b/src/version.c
index 0b62f9cae..f63041ec4 100644
--- a/src/version.c
+++ b/src/version.c
@@ -762,10 +762,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    56,
 /**/
     55,
 /**/
     54,
 /**/

Vulnerable code: 	/* load or unload key mapping tables */
	errmsg = keymap_init();
Fixed code: /*
 * Return TRUE if "val" is a valid 'filetype' name.
 * Also used for 'syntax' and 'keymap'.
 */
    static int
valid_filetype(char_u *val)
{
    char_u *s;

    for (s = val; *s != NUL; ++s)
	if (!ASCII_ISALNUM(*s) && vim_strchr((char_u *)".-_", *s) == NULL)
	    return FALSE;
    return TRUE;
}

	if (!valid_filetype(*varp))
	    errmsg = e_invarg;
	else
	    /* load or unload key mapping tables */
	    errmsg = keymap_init();
#ifdef FEAT_AUTOCMD
    else if (gvarp == &p_ft)
    {
	if (!valid_filetype(*varp))
	    errmsg = e_invarg;
    }
#endif

#ifdef FEAT_SYN_HL
    else if (gvarp == &p_syn)
    {
	if (!valid_filetype(*varp))
	    errmsg = e_invarg;
    }
#endif

func Test_filetype_valid()
  set ft=valid_name
  call assert_equal("valid_name", &filetype)
  set ft=valid-name
  call assert_equal("valid-name", &filetype)

  call assert_fails(":set ft=wrong;name", "E474:")
  call assert_fails(":set ft=wrong\\\\name", "E474:")
  call assert_fails(":set ft=wrong\\|name", "E474:")
  call assert_fails(":set ft=wrong/name", "E474:")
  call assert_fails(":set ft=wrong\\\nname", "E474:")
  call assert_equal("valid-name", &filetype)

  exe "set ft=trunc\x00name"
  call assert_equal("trunc", &filetype)
endfunc

func Test_syntax_valid()
  set syn=valid_name
  call assert_equal("valid_name", &syntax)
  set syn=valid-name
  call assert_equal("valid-name", &syntax)

  call assert_fails(":set syn=wrong;name", "E474:")
  call assert_fails(":set syn=wrong\\\\name", "E474:")
  call assert_fails(":set syn=wrong\\|name", "E474:")
  call assert_fails(":set syn=wrong/name", "E474:")
  call assert_fails(":set syn=wrong\\\nname", "E474:")
  call assert_equal("valid-name", &syntax)

  exe "set syn=trunc\x00name"
  call assert_equal("trunc", &syntax)
endfunc

func Test_keymap_valid()
  call assert_fails(":set kmp=valid_name", "E544:")
  call assert_fails(":set kmp=valid_name", "valid_name")
  call assert_fails(":set kmp=valid-name", "E544:")
  call assert_fails(":set kmp=valid-name", "valid-name")

  call assert_fails(":set kmp=wrong;name", "E474:")
  call assert_fails(":set kmp=wrong\\\\name", "E474:")
  call assert_fails(":set kmp=wrong\\|name", "E474:")
  call assert_fails(":set kmp=wrong/name", "E474:")
  call assert_fails(":set kmp=wrong\\\nname", "E474:")

  call assert_fails(":set kmp=trunc\x00name", "E544:")
  call assert_fails(":set kmp=trunc\x00name", "trunc")
endfunc
/**/
    56,