comparison src/optionstr.c @ 33864:6e4c686b6b5b v9.0.2142

patch 9.0.2142: [security]: stack-buffer-overflow in option callback functions Commit: https://github.com/vim/vim/commit/b39b240c386a5a29241415541f1c99e2e6b8ce47 Author: Christian Brabandt <cb@256bit.org> Date: Wed Nov 29 11:34:05 2023 +0100 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>
author Christian Brabandt <cb@256bit.org>
date Sun, 10 Dec 2023 15:16:04 +0100
parents e628d7f03758
children 2f9090601258
comparison
equal deleted inserted replaced
33863:3b8089d550eb 33864:6e4c686b6b5b
227 reset_v_option_vars(); 227 reset_v_option_vars();
228 } 228 }
229 #endif 229 #endif
230 230
231 static char * 231 static char *
232 illegal_char(char *errbuf, int c) 232 illegal_char(char *errbuf, int errbuflen, int c)
233 { 233 {
234 if (errbuf == NULL) 234 if (errbuf == NULL)
235 return ""; 235 return "";
236 sprintf((char *)errbuf, _(e_illegal_character_str), (char *)transchar(c)); 236 snprintf((char *)errbuf, errbuflen, _(e_illegal_character_str),
237 (char *)transchar(c));
237 return errbuf; 238 return errbuf;
238 } 239 }
239 240
240 /* 241 /*
241 * Check string options in a buffer for NULL value. 242 * Check string options in a buffer for NULL value.
523 char * 524 char *
524 set_string_option( 525 set_string_option(
525 int opt_idx, 526 int opt_idx,
526 char_u *value, 527 char_u *value,
527 int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL 528 int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL
528 char *errbuf) 529 char *errbuf,
530 int errbuflen)
529 { 531 {
530 char_u *s; 532 char_u *s;
531 char_u **varp; 533 char_u **varp;
532 char_u *oldval; 534 char_u *oldval;
533 #if defined(FEAT_EVAL) 535 #if defined(FEAT_EVAL)
577 saved_oldval = vim_strsave(oldval); 579 saved_oldval = vim_strsave(oldval);
578 saved_newval = vim_strsave(s); 580 saved_newval = vim_strsave(s);
579 } 581 }
580 #endif 582 #endif
581 if ((errmsg = did_set_string_option(opt_idx, varp, oldval, value, errbuf, 583 if ((errmsg = did_set_string_option(opt_idx, varp, oldval, value, errbuf,
582 opt_flags, OP_NONE, &value_checked)) == NULL) 584 errbuflen, opt_flags, OP_NONE, &value_checked)) == NULL)
583 did_set_option(opt_idx, opt_flags, TRUE, value_checked); 585 did_set_option(opt_idx, opt_flags, TRUE, value_checked);
584 586
585 #if defined(FEAT_EVAL) 587 #if defined(FEAT_EVAL)
586 // call autocommand after handling side effects 588 // call autocommand after handling side effects
587 if (errmsg == NULL) 589 if (errmsg == NULL)
613 */ 615 */
614 static char * 616 static char *
615 check_stl_option(char_u *s) 617 check_stl_option(char_u *s)
616 { 618 {
617 int groupdepth = 0; 619 int groupdepth = 0;
618 static char errbuf[80]; 620 static char errbuf[ERR_BUFLEN];
621 int errbuflen = ERR_BUFLEN;
619 622
620 while (*s) 623 while (*s)
621 { 624 {
622 // Check for valid keys after % sequences 625 // Check for valid keys after % sequences
623 while (*s && *s != '%') 626 while (*s && *s != '%')
654 groupdepth++; 657 groupdepth++;
655 continue; 658 continue;
656 } 659 }
657 if (vim_strchr(STL_ALL, *s) == NULL) 660 if (vim_strchr(STL_ALL, *s) == NULL)
658 { 661 {
659 return illegal_char(errbuf, *s); 662 return illegal_char(errbuf, errbuflen, *s);
660 } 663 }
661 if (*s == '{') 664 if (*s == '{')
662 { 665 {
663 int reevaluate = (*++s == '%'); 666 int reevaluate = (*++s == '%');
664 667
665 if (reevaluate && *++s == '}') 668 if (reevaluate && *++s == '}')
666 // "}" is not allowed immediately after "%{%" 669 // "}" is not allowed immediately after "%{%"
667 return illegal_char(errbuf, '}'); 670 return illegal_char(errbuf, errbuflen, '}');
668 while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) 671 while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s)
669 s++; 672 s++;
670 if (*s != '}') 673 if (*s != '}')
671 return e_unclosed_expression_sequence; 674 return e_unclosed_expression_sequence;
672 } 675 }
717 720
718 /* 721 /*
719 * An option which is a list of flags is set. Valid values are in 'flags'. 722 * An option which is a list of flags is set. Valid values are in 'flags'.
720 */ 723 */
721 static char * 724 static char *
722 did_set_option_listflag(char_u *val, char_u *flags, char *errbuf) 725 did_set_option_listflag(
726 char_u *val,
727 char_u *flags,
728 char *errbuf,
729 int errbuflen)
723 { 730 {
724 char_u *s; 731 char_u *s;
725 732
726 for (s = val; *s; ++s) 733 for (s = val; *s; ++s)
727 if (vim_strchr(flags, *s) == NULL) 734 if (vim_strchr(flags, *s) == NULL)
728 return illegal_char(errbuf, *s); 735 return illegal_char(errbuf, errbuflen, *s);
729 736
730 return NULL; 737 return NULL;
731 } 738 }
732 739
733 /* 740 /*
1459 while (*s && *s != ':') 1466 while (*s && *s != ':')
1460 { 1467 {
1461 if (vim_strchr((char_u *)COM_ALL, *s) == NULL 1468 if (vim_strchr((char_u *)COM_ALL, *s) == NULL
1462 && !VIM_ISDIGIT(*s) && *s != '-') 1469 && !VIM_ISDIGIT(*s) && *s != '-')
1463 { 1470 {
1464 errmsg = illegal_char(args->os_errbuf, *s); 1471 errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, *s);
1465 break; 1472 break;
1466 } 1473 }
1467 ++s; 1474 ++s;
1468 } 1475 }
1469 if (*s++ == NUL) 1476 if (*s++ == NUL)
1515 while (*s == ',' || *s == ' ') 1522 while (*s == ',' || *s == ' ')
1516 s++; 1523 s++;
1517 if (!*s) 1524 if (!*s)
1518 break; 1525 break;
1519 if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL) 1526 if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL)
1520 return illegal_char(args->os_errbuf, *s); 1527 return illegal_char(args->os_errbuf, args->os_errbuflen, *s);
1521 if (*++s != NUL && *s != ',' && *s != ' ') 1528 if (*++s != NUL && *s != ',' && *s != ' ')
1522 { 1529 {
1523 if (s[-1] == 'k' || s[-1] == 's') 1530 if (s[-1] == 'k' || s[-1] == 's')
1524 { 1531 {
1525 // skip optional filename after 'k' and 's' 1532 // skip optional filename after 'k' and 's'
1532 } 1539 }
1533 else 1540 else
1534 { 1541 {
1535 if (args->os_errbuf != NULL) 1542 if (args->os_errbuf != NULL)
1536 { 1543 {
1537 sprintf((char *)args->os_errbuf, 1544 snprintf((char *)args->os_errbuf, args->os_errbuflen,
1538 _(e_illegal_character_after_chr), *--s); 1545 _(e_illegal_character_after_chr), *--s);
1539 return args->os_errbuf; 1546 return args->os_errbuf;
1540 } 1547 }
1541 return ""; 1548 return "";
1542 } 1549 }
1632 char * 1639 char *
1633 did_set_concealcursor(optset_T *args) 1640 did_set_concealcursor(optset_T *args)
1634 { 1641 {
1635 char_u **varp = (char_u **)args->os_varp; 1642 char_u **varp = (char_u **)args->os_varp;
1636 1643
1637 return did_set_option_listflag(*varp, (char_u *)COCU_ALL, args->os_errbuf); 1644 return did_set_option_listflag(*varp, (char_u *)COCU_ALL, args->os_errbuf,
1645 args->os_errbuflen);
1638 } 1646 }
1639 1647
1640 int 1648 int
1641 expand_set_concealcursor(optexpand_T *args, int *numMatches, char_u ***matches) 1649 expand_set_concealcursor(optexpand_T *args, int *numMatches, char_u ***matches)
1642 { 1650 {
1650 char * 1658 char *
1651 did_set_cpoptions(optset_T *args) 1659 did_set_cpoptions(optset_T *args)
1652 { 1660 {
1653 char_u **varp = (char_u **)args->os_varp; 1661 char_u **varp = (char_u **)args->os_varp;
1654 1662
1655 return did_set_option_listflag(*varp, (char_u *)CPO_ALL, args->os_errbuf); 1663 return did_set_option_listflag(*varp, (char_u *)CPO_ALL, args->os_errbuf,
1664 args->os_errbuflen);
1656 } 1665 }
1657 1666
1658 int 1667 int
1659 expand_set_cpoptions(optexpand_T *args, int *numMatches, char_u ***matches) 1668 expand_set_cpoptions(optexpand_T *args, int *numMatches, char_u ***matches)
1660 { 1669 {
2279 char * 2288 char *
2280 did_set_formatoptions(optset_T *args) 2289 did_set_formatoptions(optset_T *args)
2281 { 2290 {
2282 char_u **varp = (char_u **)args->os_varp; 2291 char_u **varp = (char_u **)args->os_varp;
2283 2292
2284 return did_set_option_listflag(*varp, (char_u *)FO_ALL, args->os_errbuf); 2293 return did_set_option_listflag(*varp, (char_u *)FO_ALL, args->os_errbuf,
2294 args->os_errbuflen);
2285 } 2295 }
2286 2296
2287 int 2297 int
2288 expand_set_formatoptions(optexpand_T *args, int *numMatches, char_u ***matches) 2298 expand_set_formatoptions(optexpand_T *args, int *numMatches, char_u ***matches)
2289 { 2299 {
2420 did_set_guioptions(optset_T *args) 2430 did_set_guioptions(optset_T *args)
2421 { 2431 {
2422 char_u **varp = (char_u **)args->os_varp; 2432 char_u **varp = (char_u **)args->os_varp;
2423 char *errmsg; 2433 char *errmsg;
2424 2434
2425 errmsg = did_set_option_listflag(*varp, (char_u *)GO_ALL, args->os_errbuf); 2435 errmsg = did_set_option_listflag(*varp, (char_u *)GO_ALL, args->os_errbuf,
2436 args->os_errbuflen);
2426 if (errmsg != NULL) 2437 if (errmsg != NULL)
2427 return errmsg; 2438 return errmsg;
2428 2439
2429 gui_init_which_components(args->os_oldval.string); 2440 gui_init_which_components(args->os_oldval.string);
2430 return NULL; 2441 return NULL;
2924 char * 2935 char *
2925 did_set_mouse(optset_T *args) 2936 did_set_mouse(optset_T *args)
2926 { 2937 {
2927 char_u **varp = (char_u **)args->os_varp; 2938 char_u **varp = (char_u **)args->os_varp;
2928 2939
2929 return did_set_option_listflag(*varp, (char_u *)MOUSE_ALL, 2940 return did_set_option_listflag(*varp, (char_u *)MOUSE_ALL, args->os_errbuf,
2930 args->os_errbuf); 2941 args->os_errbuflen);
2931 } 2942 }
2932 2943
2933 int 2944 int
2934 expand_set_mouse(optexpand_T *args, int *numMatches, char_u ***matches) 2945 expand_set_mouse(optexpand_T *args, int *numMatches, char_u ***matches)
2935 { 2946 {
3362 char * 3373 char *
3363 did_set_shortmess(optset_T *args) 3374 did_set_shortmess(optset_T *args)
3364 { 3375 {
3365 char_u **varp = (char_u **)args->os_varp; 3376 char_u **varp = (char_u **)args->os_varp;
3366 3377
3367 return did_set_option_listflag(*varp, (char_u *)SHM_ALL, args->os_errbuf); 3378 return did_set_option_listflag(*varp, (char_u *)SHM_ALL, args->os_errbuf,
3379 args->os_errbuflen);
3368 } 3380 }
3369 3381
3370 int 3382 int
3371 expand_set_shortmess(optexpand_T *args, int *numMatches, char_u ***matches) 3383 expand_set_shortmess(optexpand_T *args, int *numMatches, char_u ***matches)
3372 { 3384 {
4028 for (s = p_viminfo; *s;) 4040 for (s = p_viminfo; *s;)
4029 { 4041 {
4030 // Check it's a valid character 4042 // Check it's a valid character
4031 if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) 4043 if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL)
4032 { 4044 {
4033 errmsg = illegal_char(args->os_errbuf, *s); 4045 errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, *s);
4034 break; 4046 break;
4035 } 4047 }
4036 if (*s == 'n') // name is always last one 4048 if (*s == 'n') // name is always last one
4037 break; 4049 break;
4038 else if (*s == 'r') // skip until next ',' 4050 else if (*s == 'r') // skip until next ','
4055 4067
4056 if (!VIM_ISDIGIT(*(s - 1))) 4068 if (!VIM_ISDIGIT(*(s - 1)))
4057 { 4069 {
4058 if (args->os_errbuf != NULL) 4070 if (args->os_errbuf != NULL)
4059 { 4071 {
4060 sprintf(args->os_errbuf, 4072 snprintf(args->os_errbuf, args->os_errbuflen,
4061 _(e_missing_number_after_angle_str_angle), 4073 _(e_missing_number_after_angle_str_angle),
4062 transchar_byte(*(s - 1))); 4074 transchar_byte(*(s - 1)));
4063 errmsg = args->os_errbuf; 4075 errmsg = args->os_errbuf;
4064 } 4076 }
4065 else 4077 else
4138 { 4150 {
4139 char_u **varp = (char_u **)args->os_varp; 4151 char_u **varp = (char_u **)args->os_varp;
4140 4152
4141 // Add ',' to the list flags because 'whichwrap' is a flag 4153 // Add ',' to the list flags because 'whichwrap' is a flag
4142 // list that is comma-separated. 4154 // list that is comma-separated.
4143 return did_set_option_listflag(*varp, (char_u *)(WW_ALL ","), args->os_errbuf); 4155 return did_set_option_listflag(*varp, (char_u *)(WW_ALL ","),
4156 args->os_errbuf, args->os_errbuflen);
4144 } 4157 }
4145 4158
4146 int 4159 int
4147 expand_set_whichwrap(optexpand_T *args, int *numMatches, char_u ***matches) 4160 expand_set_whichwrap(optexpand_T *args, int *numMatches, char_u ***matches)
4148 { 4161 {
4339 int opt_idx, // index in options[] table 4352 int opt_idx, // index in options[] table
4340 char_u **varp, // pointer to the option variable 4353 char_u **varp, // pointer to the option variable
4341 char_u *oldval, // previous value of the option 4354 char_u *oldval, // previous value of the option
4342 char_u *value, // new value of the option 4355 char_u *value, // new value of the option
4343 char *errbuf, // buffer for errors, or NULL 4356 char *errbuf, // buffer for errors, or NULL
4357 int errbuflen, // length of error buffer
4344 int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL 4358 int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL
4345 set_op_T op, // OP_ADDING/OP_PREPENDING/OP_REMOVING 4359 set_op_T op, // OP_ADDING/OP_PREPENDING/OP_REMOVING
4346 int *value_checked) // value was checked to be safe, no 4360 int *value_checked) // value was checked to be safe, no
4347 // need to set P_INSECURE 4361 // need to set P_INSECURE
4348 { 4362 {
4383 args.os_flags = opt_flags; 4397 args.os_flags = opt_flags;
4384 args.os_op = op; 4398 args.os_op = op;
4385 args.os_oldval.string = oldval; 4399 args.os_oldval.string = oldval;
4386 args.os_newval.string = value; 4400 args.os_newval.string = value;
4387 args.os_errbuf = errbuf; 4401 args.os_errbuf = errbuf;
4402 args.os_errbuflen = errbuflen;
4388 // Invoke the option specific callback function to validate and apply 4403 // Invoke the option specific callback function to validate and apply
4389 // the new option value. 4404 // the new option value.
4390 errmsg = did_set_cb(&args); 4405 errmsg = did_set_cb(&args);
4391 4406
4392 // The 'keymap', 'filetype' and 'syntax' option callback functions 4407 // The 'keymap', 'filetype' and 'syntax' option callback functions