CVE-2024-22667: Out-of-bounds Write (CWE-787)
value: Vim before 9.0.2142 has a stack-based buffer overflow because did_set_langmap in map.c calls sprintf to write to the error buffer that is passed down to the option callback functions.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.8
Published: 2024-02-05T08:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/map.c, src/option.c, src/option.h, src/optionstr.c, src/proto/optionstr.pro, src/structs.h, src/testdir/crash/poc_did_set_langmap, src/testdir/test_crash.vim, src/version.c
Commit date: 2023-12-01 18:58:51 +0100
Commit message: patch 9.0.2142: [security]: stack-buffer-overflow in option callback functions

Problem:  [security]: stack-buffer-overflow in option callback functions
Solution: pass size of errbuf down the call stack, use snprintf()
          instead of sprintf()

We pass the error buffer down to the option callback functions, but in
some parts of the code, we simply use sprintf(buf) to write into the error
buffer, which can overflow.

So let's pass down the length of the error buffer and use sprintf(buf, size)
instead.

Reported by @henices, thanks!

Signed-off-by: Christian Brabandt <cb@256bit.org>

Diff: diff --git a/src/map.c b/src/map.c
index 5988445bd158..98785e722ce5 100644
--- a/src/map.c
+++ b/src/map.c
@@ -3112,11 +3112,11 @@ did_set_langmap(optset_T *args UNUSED)
 		    p = p2;
 		    if (p[0] != NUL)
 		    {
 			if (p[0] != ',')
 			{
-			    sprintf(args->os_errbuf,
+			    snprintf(args->os_errbuf, args->os_errbuflen,
 				    _(e_langmap_extra_characters_after_semicolon_str),
 				    p);
 			    return args->os_errbuf;
 			}
 			++p;
diff --git a/src/option.c b/src/option.c
index d5d20d7674aa..572788559cdf 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1930,10 +1930,11 @@ do_set_option_string(
 	set_op_T    op_arg,
 	long_u	    flags,
 	int	    cp_val,
 	char_u	    *varp_arg,
 	char	    *errbuf,
+	int	    errbuflen,
 	int	    *value_checked,
 	char	    **errmsg)
 {
     char_u	*arg = *argp;
     set_op_T    op = op_arg;
@@ -2028,11 +2029,11 @@ do_set_option_string(
 	// Handle side effects, and set the global value for ":set" on local
 	// options. Note: when setting 'syntax' or 'filetype' autocommands may
 	// be triggered that can cause havoc.
 	*errmsg = did_set_string_option(
 			opt_idx, (char_u **)varp, oldval, newval, errbuf,
-			opt_flags, op, value_checked);
+			errbuflen, opt_flags, op, value_checked);
 
 	secure = secure_saved;
     }
 
 #if defined(FEAT_EVAL)
@@ -2285,11 +2286,11 @@ do_set_option_value(
 	}
 	else if (opt_idx >= 0)
 	{
 	    // string option
 	    if (do_set_option_string(opt_idx, opt_flags, &arg, nextchar, op,
-					flags, cp_val, varp, errbuf,
+					flags, cp_val, varp, errbuf, errbuflen,
 					&value_checked, &errmsg) == FAIL)
 	    {
 		if (errmsg != NULL)
 		    goto skip;
 		*stopopteval = TRUE;
@@ -2577,16 +2578,16 @@ do_set(
 	}
 	else
 	{
 	    int		stopopteval = FALSE;
 	    char	*errmsg = NULL;
-	    char	errbuf[80];
+	    char	errbuf[ERR_BUFLEN];
 	    char_u	*startarg = arg;
 
 	    errmsg = do_set_option(opt_flags, &arg, arg_start, &startarg,
 					&did_show, &stopopteval, errbuf,
-					sizeof(errbuf));
+					ERR_BUFLEN);
 	    if (stopopteval)
 		break;
 
 	    // Advance to next argument.
 	    // - skip until a blank found, taking care of backslashes
@@ -5345,11 +5346,12 @@ set_option_value(
     int		opt_flags)	// OPT_LOCAL or 0 (both)
 {
     int		opt_idx;
     char_u	*varp;
     long_u	flags;
-    static char	errbuf[80];
+    static char	errbuf[ERR_BUFLEN];
+    int		errbuflen = ERR_BUFLEN;
 
     opt_idx = findoption(name);
     if (opt_idx < 0)
     {
 	int key;
@@ -5388,11 +5390,11 @@ set_option_value(
 	    emsg(_(e_not_allowed_in_sandbox));
 	    return NULL;
 	}
 #endif
 	if (flags & P_STRING)
-	    return set_string_option(opt_idx, string, opt_flags, errbuf);
+	    return set_string_option(opt_idx, string, opt_flags, errbuf, errbuflen);
 
 	varp = get_varp_scope(&(options[opt_idx]), opt_flags);
 	if (varp != NULL)	// hidden option is not changed
 	{
 	    if (number == 0 && string != NULL)
diff --git a/src/option.h b/src/option.h
index 2d1ca2cdeaf6..646056bf111b 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1319,6 +1319,8 @@ enum
 };
 
 // Value for b_p_ul indicating the global value must be used.
 #define NO_LOCAL_UNDOLEVEL (-123456)
 
+#define ERR_BUFLEN 80
+
 #endif // _OPTION_H_
diff --git a/src/optionstr.c b/src/optionstr.c
index b7cdcc4511c7..84c77cb0a0e2 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -227,15 +227,16 @@ trigger_optionset_string(
     reset_v_option_vars();
 }
 #endif
 
     static char *
-illegal_char(char *errbuf, int c)
+illegal_char(char *errbuf, int errbuflen, int c)
 {
     if (errbuf == NULL)
 	return "";
-    sprintf((char *)errbuf, _(e_illegal_character_str), (char *)transchar(c));
+    snprintf((char *)errbuf, errbuflen, _(e_illegal_character_str),
+		    (char *)transchar(c));
     return errbuf;
 }
 
 /*
  * Check string options in a buffer for NULL value.
@@ -523,11 +524,12 @@ set_string_option_direct_in_buf(
     char *
 set_string_option(
     int		opt_idx,
     char_u	*value,
     int		opt_flags,	// OPT_LOCAL and/or OPT_GLOBAL
-    char	*errbuf)
+    char	*errbuf,
+    int		errbuflen)
 {
     char_u	*s;
     char_u	**varp;
     char_u	*oldval;
 #if defined(FEAT_EVAL)
@@ -577,11 +579,11 @@ set_string_option(
 	saved_oldval = vim_strsave(oldval);
 	saved_newval = vim_strsave(s);
     }
 #endif
     if ((errmsg = did_set_string_option(opt_idx, varp, oldval, value, errbuf,
-		    opt_flags, OP_NONE, &value_checked)) == NULL)
+		    errbuflen, opt_flags, OP_NONE, &value_checked)) == NULL)
 	did_set_option(opt_idx, opt_flags, TRUE, value_checked);
 
 #if defined(FEAT_EVAL)
     // call autocommand after handling side effects
     if (errmsg == NULL)
@@ -613,11 +615,12 @@ valid_filetype(char_u *val)
  */
     static char *
 check_stl_option(char_u *s)
 {
     int		groupdepth = 0;
-    static char errbuf[80];
+    static char errbuf[ERR_BUFLEN];
+    int		errbuflen = ERR_BUFLEN;
 
     while (*s)
     {
 	// Check for valid keys after % sequences
 	while (*s && *s != '%')
@@ -654,19 +657,19 @@ check_stl_option(char_u *s)
 	    groupdepth++;
 	    continue;
 	}
 	if (vim_strchr(STL_ALL, *s) == NULL)
 	{
-	    return illegal_char(errbuf, *s);
+	    return illegal_char(errbuf, errbuflen, *s);
 	}
 	if (*s == '{')
 	{
 	    int reevaluate = (*++s == '%');
 
 	    if (reevaluate && *++s == '}')
 		// "}" is not allowed immediately after "%{%"
-		return illegal_char(errbuf, '}');
+		return illegal_char(errbuf, errbuflen, '}');
 	    while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s)
 		s++;
 	    if (*s != '}')
 		return e_unclosed_expression_sequence;
 	}
@@ -717,17 +720,21 @@ did_set_opt_strings(char_u *val, char **values, int list)
 
 /*
  * An option which is a list of flags is set.  Valid values are in 'flags'.
  */
     static char *
-did_set_option_listflag(char_u *val, char_u *flags, char *errbuf)
+did_set_option_listflag(
+	char_u *val,
+	char_u *flags,
+	char *errbuf,
+	int errbuflen)
 {
     char_u	*s;
 
     for (s = val; *s; ++s)
 	if (vim_strchr(flags, *s) == NULL)
-	    return illegal_char(errbuf, *s);
+	    return illegal_char(errbuf, errbuflen, *s);
 
     return NULL;
 }
 
 /*
@@ -1459,11 +1466,11 @@ did_set_comments(optset_T *args)
 	while (*s && *s != ':')
 	{
 	    if (vim_strchr((char_u *)COM_ALL, *s) == NULL
 		    && !VIM_ISDIGIT(*s) && *s != '-')
 	    {
-		errmsg = illegal_char(args->os_errbuf, *s);
+		errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, *s);
 		break;
 	    }
 	    ++s;
 	}
 	if (*s++ == NUL)
@@ -1515,11 +1522,11 @@ did_set_complete(optset_T *args)
 	while (*s == ',' || *s == ' ')
 	    s++;
 	if (!*s)
 	    break;
 	if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL)
-	    return illegal_char(args->os_errbuf, *s);
+	    return illegal_char(args->os_errbuf, args->os_errbuflen, *s);
 	if (*++s != NUL && *s != ',' && *s != ' ')
 	{
 	    if (s[-1] == 'k' || s[-1] == 's')
 	    {
 		// skip optional filename after 'k' and 's'
@@ -1532,11 +1539,11 @@ did_set_complete(optset_T *args)
 	    }
 	    else
 	    {
 		if (args->os_errbuf != NULL)
 		{
-		    sprintf((char *)args->os_errbuf,
+		    snprintf((char *)args->os_errbuf, args->os_errbuflen,
 			    _(e_illegal_character_after_chr), *--s);
 		    return args->os_errbuf;
 		}
 		return "";
 	    }
@@ -1632,11 +1639,12 @@ expand_set_completeslash(optexpand_T *args, int *numMatches, char_u ***matches)
     char *
 did_set_concealcursor(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
-    return did_set_option_listflag(*varp, (char_u *)COCU_ALL, args->os_errbuf);
+    return did_set_option_listflag(*varp, (char_u *)COCU_ALL, args->os_errbuf,
+		    args->os_errbuflen);
 }
 
     int
 expand_set_concealcursor(optexpand_T *args, int *numMatches, char_u ***matches)
 {
@@ -1650,11 +1658,12 @@ expand_set_concealcursor(optexpand_T *args, int *numMatches, char_u ***matches)
     char *
 did_set_cpoptions(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
-    return did_set_option_listflag(*varp, (char_u *)CPO_ALL, args->os_errbuf);
+    return did_set_option_listflag(*varp, (char_u *)CPO_ALL, args->os_errbuf,
+		    args->os_errbuflen);
 }
 
     int
 expand_set_cpoptions(optexpand_T *args, int *numMatches, char_u ***matches)
 {
@@ -2279,11 +2288,12 @@ expand_set_foldopen(optexpand_T *args, int *numMatches, char_u ***matches)
     char *
 did_set_formatoptions(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
-    return did_set_option_listflag(*varp, (char_u *)FO_ALL, args->os_errbuf);
+    return did_set_option_listflag(*varp, (char_u *)FO_ALL, args->os_errbuf,
+		    args->os_errbuflen);
 }
 
     int
 expand_set_formatoptions(optexpand_T *args, int *numMatches, char_u ***matches)
 {
@@ -2420,11 +2430,12 @@ did_set_guiligatures(optset_T *args UNUSED)
 did_set_guioptions(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
     char *errmsg;
 
-    errmsg = did_set_option_listflag(*varp, (char_u *)GO_ALL, args->os_errbuf);
+    errmsg = did_set_option_listflag(*varp, (char_u *)GO_ALL, args->os_errbuf,
+		    args->os_errbuflen);
     if (errmsg != NULL)
 	return errmsg;
 
     gui_init_which_components(args->os_oldval.string);
     return NULL;
@@ -2924,12 +2935,12 @@ did_set_mkspellmem(optset_T *args UNUSED)
     char *
 did_set_mouse(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
-    return did_set_option_listflag(*varp, (char_u *)MOUSE_ALL,
-							args->os_errbuf);
+    return did_set_option_listflag(*varp, (char_u *)MOUSE_ALL, args->os_errbuf,
+		    args->os_errbuflen);
 }
 
     int
 expand_set_mouse(optexpand_T *args, int *numMatches, char_u ***matches)
 {
@@ -3362,11 +3373,12 @@ expand_set_sessionoptions(optexpand_T *args, int *numMatches, char_u ***matches)
     char *
 did_set_shortmess(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
-    return did_set_option_listflag(*varp, (char_u *)SHM_ALL, args->os_errbuf);
+    return did_set_option_listflag(*varp, (char_u *)SHM_ALL, args->os_errbuf,
+		    args->os_errbuflen);
 }
 
     int
 expand_set_shortmess(optexpand_T *args, int *numMatches, char_u ***matches)
 {
@@ -4028,11 +4040,11 @@ did_set_viminfo(optset_T *args)
     for (s = p_viminfo; *s;)
     {
 	// Check it's a valid character
 	if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL)
 	{
-	    errmsg = illegal_char(args->os_errbuf, *s);
+	    errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, *s);
 	    break;
 	}
 	if (*s == 'n')	// name is always last one
 	    break;
 	else if (*s == 'r') // skip until next ','
@@ -4055,11 +4067,11 @@ did_set_viminfo(optset_T *args)
 
 	    if (!VIM_ISDIGIT(*(s - 1)))
 	    {
 		if (args->os_errbuf != NULL)
 		{
-		    sprintf(args->os_errbuf,
+		    snprintf(args->os_errbuf, args->os_errbuflen,
 			    _(e_missing_number_after_angle_str_angle),
 			    transchar_byte(*(s - 1)));
 		    errmsg = args->os_errbuf;
 		}
 		else
@@ -4138,11 +4150,12 @@ did_set_whichwrap(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
     // Add ',' to the list flags because 'whichwrap' is a flag
     // list that is comma-separated.
-    return did_set_option_listflag(*varp, (char_u *)(WW_ALL ","), args->os_errbuf);
+    return did_set_option_listflag(*varp, (char_u *)(WW_ALL ","),
+		    args->os_errbuf, args->os_errbuflen);
 }
 
     int
 expand_set_whichwrap(optexpand_T *args, int *numMatches, char_u ***matches)
 {
@@ -4339,10 +4352,11 @@ did_set_string_option(
     int		opt_idx,		// index in options[] table
     char_u	**varp,			// pointer to the option variable
     char_u	*oldval,		// previous value of the option
     char_u	*value,			// new value of the option
     char	*errbuf,		// buffer for errors, or NULL
+    int		errbuflen,		// length of error buffer
     int		opt_flags,		// OPT_LOCAL and/or OPT_GLOBAL
     set_op_T    op,			// OP_ADDING/OP_PREPENDING/OP_REMOVING
     int		*value_checked)		// value was checked to be safe, no
 					// need to set P_INSECURE
 {
@@ -4383,10 +4397,11 @@ did_set_string_option(
 	args.os_flags = opt_flags;
 	args.os_op = op;
 	args.os_oldval.string = oldval;
 	args.os_newval.string = value;
 	args.os_errbuf = errbuf;
+	args.os_errbuflen = errbuflen;
 	// Invoke the option specific callback function to validate and apply
 	// the new option value.
 	errmsg = did_set_cb(&args);
 
 	// The 'keymap', 'filetype' and 'syntax' option callback functions
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index 22601ba996b8..4ce93212dd94 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -6,11 +6,11 @@ void free_string_option(char_u *p);
 void clear_string_option(char_u **pp);
 void check_string_option(char_u **pp);
 void set_string_option_direct(char_u *name, int opt_idx, char_u *val, int opt_flags, int set_sid);
 void set_string_option_direct_in_win(win_T *wp, char_u *name, int opt_idx, char_u *val, int opt_flags, int set_sid);
 void set_string_option_direct_in_buf(buf_T *buf, char_u *name, int opt_idx, char_u *val, int opt_flags, int set_sid);
-char *set_string_option(int opt_idx, char_u *value, int opt_flags, char *errbuf);
+char *set_string_option(int opt_idx, char_u *value, int opt_flags, char *errbuf, int errbuflen);
 char *did_set_ambiwidth(optset_T *args);
 char *did_set_background(optset_T *args);
 char *did_set_backspace(optset_T *args);
 char *did_set_backupcopy(optset_T *args);
 char *did_set_backupext_or_patchmode(optset_T *args);
@@ -119,11 +119,11 @@ char *did_set_virtualedit(optset_T *args);
 char *did_set_whichwrap(optset_T *args);
 char *did_set_wildmode(optset_T *args);
 char *did_set_wildoptions(optset_T *args);
 char *did_set_winaltkeys(optset_T *args);
 char *did_set_wincolor(optset_T *args);
-char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char_u *value, char *errbuf, int opt_flags, set_op_T op, int *value_checked);
+char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char_u *value, char *errbuf, int errbuflen, int opt_flags, set_op_T op, int *value_checked);
 int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char_u ***matches);
 int expand_set_background(optexpand_T *args, int *numMatches, char_u ***matches);
 int expand_set_backspace(optexpand_T *args, int *numMatches, char_u ***matches);
 int expand_set_backupcopy(optexpand_T *args, int *numMatches, char_u ***matches);
 int expand_set_belloff(optexpand_T *args, int *numMatches, char_u ***matches);
diff --git a/src/structs.h b/src/structs.h
index 4e081b860ef4..6d9dcbb2ab17 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -4966,10 +4966,12 @@ typedef struct
 
     // If the value specified for an option is not valid and the error message
     // is parameterized, then the "os_errbuf" buffer is used to store the error
     // message (when it is not NULL).
     char	*os_errbuf;
+    // length of the error buffer
+    int		os_errbuflen;
 } optset_T;
 
 /*
  * Argument for the callback function (opt_expand_cb_T) invoked after a string
  * option value is expanded for cmdline completion.
diff --git a/src/testdir/crash/poc_did_set_langmap b/src/testdir/crash/poc_did_set_langmap
new file mode 100644
index 000000000000..f77145b9d1f0
--- /dev/null
+++ b/src/testdir/crash/poc_did_set_langmap
@@ -0,0 +1 @@
+se lmap=°xÿ7sil;drlmap=°xÿ7sil;drmo: pm313"
\ No newline at end of file
diff --git a/src/testdir/test_crash.vim b/src/testdir/test_crash.vim
index eef1731454d9..1d4f435e4d8b 100644
--- a/src/testdir/test_crash.vim
+++ b/src/testdir/test_crash.vim
@@ -140,19 +140,27 @@ func Test_crash1_2()
   let args = printf(cmn_args, vim, file)
   call term_sendkeys(buf, args ..
     \ '  && echo "crash 3: [OK]" >> '.. result .. "\<cr>")
   call TermWait(buf, 150)
 
+  let file = 'crash/poc_did_set_langmap'
+  let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
+  let args = printf(cmn_args, vim, file)
+  call term_sendkeys(buf, args ..
+    \ ' ; echo "crash 4: [OK]" >> '.. result .. "\<cr>")
+  call TermWait(buf, 150)
+
   " clean up
   exe buf .. "bw!"
 
   exe "sp " .. result
 
   let expected = [
       \ 'crash 1: [OK]',
       \ 'crash 2: [OK]',
       \ 'crash 3: [OK]',
+      \ 'crash 4: [OK]',
       \ ]
 
   call assert_equal(expected, getline(1, '$'))
   bw!
 
diff --git a/src/version.c b/src/version.c
index ecb5c2230494..28325a7fa4f7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -702,10 +702,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2142,
 /**/
     2141,
 /**/
     2140,
 /**/

Vulnerable code: 			    sprintf(args->os_errbuf,
			opt_flags, op, value_checked);
					flags, cp_val, varp, errbuf,
	    char	errbuf[80];
					sizeof(errbuf));
    static char	errbuf[80];
	    return set_string_option(opt_idx, string, opt_flags, errbuf);
illegal_char(char *errbuf, int c)
    sprintf((char *)errbuf, _(e_illegal_character_str), (char *)transchar(c));
    char	*errbuf)
		    opt_flags, OP_NONE, &value_checked)) == NULL)
    static char errbuf[80];
	    return illegal_char(errbuf, *s);
		return illegal_char(errbuf, '}');
did_set_option_listflag(char_u *val, char_u *flags, char *errbuf)
	    return illegal_char(errbuf, *s);
		errmsg = illegal_char(args->os_errbuf, *s);
	    return illegal_char(args->os_errbuf, *s);
		    sprintf((char *)args->os_errbuf,
    return did_set_option_listflag(*varp, (char_u *)COCU_ALL, args->os_errbuf);
    return did_set_option_listflag(*varp, (char_u *)CPO_ALL, args->os_errbuf);
    return did_set_option_listflag(*varp, (char_u *)FO_ALL, args->os_errbuf);
    errmsg = did_set_option_listflag(*varp, (char_u *)GO_ALL, args->os_errbuf);
    return did_set_option_listflag(*varp, (char_u *)MOUSE_ALL,
							args->os_errbuf);
    return did_set_option_listflag(*varp, (char_u *)SHM_ALL, args->os_errbuf);
	    errmsg = illegal_char(args->os_errbuf, *s);
		    sprintf(args->os_errbuf,
    return did_set_option_listflag(*varp, (char_u *)(WW_ALL ","), args->os_errbuf);
char *set_string_option(int opt_idx, char_u *value, int opt_flags, char *errbuf);
char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char_u *value, char *errbuf, int opt_flags, set_op_T op, int *value_checked);
Fixed code: 			    snprintf(args->os_errbuf, args->os_errbuflen,
	int	    errbuflen,
			errbuflen, opt_flags, op, value_checked);
					flags, cp_val, varp, errbuf, errbuflen,
	    char	errbuf[ERR_BUFLEN];
					ERR_BUFLEN);
    static char	errbuf[ERR_BUFLEN];
    int		errbuflen = ERR_BUFLEN;
	    return set_string_option(opt_idx, string, opt_flags, errbuf, errbuflen);
#define ERR_BUFLEN 80

illegal_char(char *errbuf, int errbuflen, int c)
    snprintf((char *)errbuf, errbuflen, _(e_illegal_character_str),
		    (char *)transchar(c));
    char	*errbuf,
    int		errbuflen)
		    errbuflen, opt_flags, OP_NONE, &value_checked)) == NULL)
    static char errbuf[ERR_BUFLEN];
    int		errbuflen = ERR_BUFLEN;
	    return illegal_char(errbuf, errbuflen, *s);
		return illegal_char(errbuf, errbuflen, '}');
did_set_option_listflag(
	char_u *val,
	char_u *flags,
	char *errbuf,
	int errbuflen)
	    return illegal_char(errbuf, errbuflen, *s);
		errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, *s);
	    return illegal_char(args->os_errbuf, args->os_errbuflen, *s);
		    snprintf((char *)args->os_errbuf, args->os_errbuflen,
    return did_set_option_listflag(*varp, (char_u *)COCU_ALL, args->os_errbuf,
		    args->os_errbuflen);
    return did_set_option_listflag(*varp, (char_u *)CPO_ALL, args->os_errbuf,
		    args->os_errbuflen);
    return did_set_option_listflag(*varp, (char_u *)FO_ALL, args->os_errbuf,
		    args->os_errbuflen);
    errmsg = did_set_option_listflag(*varp, (char_u *)GO_ALL, args->os_errbuf,
		    args->os_errbuflen);
    return did_set_option_listflag(*varp, (char_u *)MOUSE_ALL, args->os_errbuf,
		    args->os_errbuflen);
    return did_set_option_listflag(*varp, (char_u *)SHM_ALL, args->os_errbuf,
		    args->os_errbuflen);
	    errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, *s);
		    snprintf(args->os_errbuf, args->os_errbuflen,
    return did_set_option_listflag(*varp, (char_u *)(WW_ALL ","),
		    args->os_errbuf, args->os_errbuflen);
    int		errbuflen,		// length of error buffer
	args.os_errbuflen = errbuflen;
char *set_string_option(int opt_idx, char_u *value, int opt_flags, char *errbuf, int errbuflen);
char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char_u *value, char *errbuf, int errbuflen, int opt_flags, set_op_T op, int *value_checked);
    // length of the error buffer
    int		os_errbuflen;
se lmap=°xÿ7sil;drlmap=°xÿ7sil;drmo: pm313"
  let file = 'crash/poc_did_set_langmap'
  let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
  let args = printf(cmn_args, vim, file)
  call term_sendkeys(buf, args ..
    \ ' ; echo "crash 4: [OK]" >> '.. result .. "\<cr>")
  call TermWait(buf, 150)

      \ 'crash 4: [OK]',
/**/
    2142,