diff --git a/backport-CVE-2024-22667.patch b/backport-CVE-2024-22667.patch new file mode 100644 index 0000000000000000000000000000000000000000..bde5c35719886f9e5b1693f94c9d4aad76578391 --- /dev/null +++ b/backport-CVE-2024-22667.patch @@ -0,0 +1,370 @@ +From b39b240c386a5a29241415541f1c99e2e6b8ce47 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Wed, 29 Nov 2023 11:34:05 +0100 +Subject: [PATCH] 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 +--- + src/map.c | 2 +- + src/option.c | 14 ++++--- + src/option.h | 2 + + src/optionstr.c | 59 +++++++++++++++++---------- + src/proto/optionstr.pro | 4 +- + src/structs.h | 2 + + 6 files changed, 52 insertions(+), 31 deletions(-) + +diff --git a/src/map.c b/src/map.c +index 5988445bd1588..98785e722ce53 100644 +--- a/src/map.c ++++ b/src/map.c +@@ -3114,7 +3114,7 @@ did_set_langmap(optset_T *args UNUSED) + { + 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; +diff --git a/src/option.c b/src/option.c +index d5d20d7674aa6..572788559cdfd 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -1932,6 +1932,7 @@ do_set_option_string( + int cp_val, + char_u *varp_arg, + char *errbuf, ++ int errbuflen, + int *value_checked, + char **errmsg) + { +@@ -2030,7 +2031,7 @@ do_set_option_string( + // 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; + } +@@ -2287,7 +2288,7 @@ do_set_option_value( + { + // 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) +@@ -2579,12 +2580,12 @@ do_set( + { + 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; + +@@ -5347,7 +5348,8 @@ set_option_value( + 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) +@@ -5390,7 +5392,7 @@ set_option_value( + } + #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 +diff --git a/src/option.h b/src/option.h +index 2d1ca2cdeaf66..646056bf111b4 100644 +--- a/src/option.h ++++ b/src/option.h +@@ -1321,4 +1321,6 @@ 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 b7cdcc4511c7c..84c77cb0a0e25 100644 +--- a/src/optionstr.c ++++ b/src/optionstr.c +@@ -229,11 +229,12 @@ trigger_optionset_string( + #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; + } + +@@ -525,7 +526,8 @@ 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; +@@ -579,7 +581,7 @@ set_string_option( + } + #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) +@@ -615,7 +617,8 @@ valid_filetype(char_u *val) + check_stl_option(char_u *s) + { + int groupdepth = 0; +- static char errbuf[80]; ++ static char errbuf[ERR_BUFLEN]; ++ int errbuflen = ERR_BUFLEN; + + while (*s) + { +@@ -656,7 +659,7 @@ check_stl_option(char_u *s) + } + if (vim_strchr(STL_ALL, *s) == NULL) + { +- return illegal_char(errbuf, *s); ++ return illegal_char(errbuf, errbuflen, *s); + } + if (*s == '{') + { +@@ -664,7 +667,7 @@ check_stl_option(char_u *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 != '}') +@@ -719,13 +722,17 @@ 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; + } +@@ -1461,7 +1468,7 @@ did_set_comments(optset_T *args) + 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; +@@ -1517,7 +1524,7 @@ did_set_complete(optset_T *args) + 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') +@@ -1534,7 +1541,7 @@ did_set_complete(optset_T *args) + { + 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; + } +@@ -1634,7 +1641,8 @@ 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 +@@ -1652,7 +1660,8 @@ 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 +@@ -2281,7 +2290,8 @@ 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 +@@ -2422,7 +2432,8 @@ 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; + +@@ -2926,8 +2937,8 @@ 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 +@@ -3364,7 +3375,8 @@ 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 +@@ -4030,7 +4042,7 @@ did_set_viminfo(optset_T *args) + // 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 +@@ -4057,7 +4069,7 @@ did_set_viminfo(optset_T *args) + { + 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; +@@ -4140,7 +4152,8 @@ did_set_whichwrap(optset_T *args) + + // 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 +@@ -4341,6 +4354,7 @@ did_set_string_option( + 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 +@@ -4385,6 +4399,7 @@ did_set_string_option( + 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); +diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro +index 22601ba996b8f..4ce93212dd948 100644 +--- a/src/proto/optionstr.pro ++++ b/src/proto/optionstr.pro +@@ -8,7 +8,7 @@ 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); +@@ -121,7 +121,7 @@ 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); +diff --git a/src/structs.h b/src/structs.h +index 4e081b860ef45..6d9dcbb2ab17d 100644 +--- a/src/structs.h ++++ b/src/structs.h +@@ -4968,6 +4968,8 @@ typedef struct + // 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; + + /* diff --git a/vim.spec b/vim.spec index f857d7b67d21c904f2bedea4d213296ef342adc7..5e14f84f147c0b7adfc0c2362e79e490a380990d 100644 --- a/vim.spec +++ b/vim.spec @@ -14,7 +14,7 @@ Name: vim Epoch: 2 Version: %{baseversion}.%{patchlevel} -Release: 4 +Release: 5 Summary: Vim is a highly configurable text editor for efficiently creating and changing any kind of text. License: Vim and MIT URL: http://www.vim.org @@ -40,6 +40,7 @@ Patch6006: backport-patch-9.0.2114-overflow-detection-not-accurate-when-add Patch6007: backport-patch-9.0.2121-use-after-free-in-ex_substitute.patch Patch6008: backport-patch-9.0.2123-Problem-with-initializing-the-length-of-range-lists.patch Patch6009: backport-vim-7.0-rclocation.patch +Patch6010: backport-CVE-2024-22667.patch Patch9000: bugfix-rm-modify-info-version.patch @@ -447,6 +448,12 @@ LC_ALL=en_US.UTF-8 make -j1 test || echo "Warning: Please check tests." %{_mandir}/man1/evim.* %changelog +* Mon Jul 08 2024 wangjiang - 2:9.0.2092-5 +- Type:CVE +- ID:CVE-2024-22667 +- SUG:NA +- DESC:fix CVE-2024-22667 + * Fri Jul 5 2024 Han Jinpeng - 2:9.0.2092-4 - Type:bugfix - ID:NA