Mercurial > vim
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 |