Mercurial > vim
comparison src/strings.c @ 34561:0adcad161c46 v9.1.0181
patch 9.1.0181: no overflow check for string formatting
Commit: https://github.com/vim/vim/commit/c35fc03dbd47582b256776fb11f11d8ceb24f8f0
Author: Christ van Willegen <cvwillegen@gmail.com>
Date: Thu Mar 14 18:30:41 2024 +0100
patch 9.1.0181: no overflow check for string formatting
Problem: no overflow check for string formatting
Solution: Check message formatting function for overflow.
(Chris van Willegen)
closes: #13799
Signed-off-by: Christ van Willegen <cvwillegen@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 14 Mar 2024 19:15:03 +0100 |
parents | da670b1549b3 |
children | a9e71b25d1b1 |
comparison
equal
deleted
inserted
replaced
34560:8b94adf4474a | 34561:0adcad161c46 |
---|---|
2466 (*ap_types)[arg - 1] = type; | 2466 (*ap_types)[arg - 1] = type; |
2467 | 2467 |
2468 return OK; | 2468 return OK; |
2469 } | 2469 } |
2470 | 2470 |
2471 static void | |
2472 format_overflow_error(const char *pstart) | |
2473 { | |
2474 size_t arglen = 0; | |
2475 char *argcopy = NULL; | |
2476 const char *p = pstart; | |
2477 | |
2478 while (VIM_ISDIGIT((int)(*p))) | |
2479 ++p; | |
2480 | |
2481 arglen = p - pstart; | |
2482 argcopy = ALLOC_CLEAR_MULT(char, arglen + 1); | |
2483 if (argcopy != NULL) | |
2484 { | |
2485 strncpy(argcopy, pstart, arglen); | |
2486 semsg(_( e_val_too_large), argcopy); | |
2487 free(argcopy); | |
2488 } | |
2489 else | |
2490 semsg(_(e_out_of_memory_allocating_nr_bytes), arglen); | |
2491 } | |
2492 | |
2493 #define MAX_ALLOWED_STRING_WIDTH 6400 | |
2494 | |
2495 static int | |
2496 get_unsigned_int( | |
2497 const char *pstart, | |
2498 const char **p, | |
2499 unsigned int *uj) | |
2500 { | |
2501 *uj = **p - '0'; | |
2502 ++*p; | |
2503 | |
2504 while (VIM_ISDIGIT((int)(**p)) && *uj < MAX_ALLOWED_STRING_WIDTH) | |
2505 { | |
2506 *uj = 10 * *uj + (unsigned int)(**p - '0'); | |
2507 ++*p; | |
2508 } | |
2509 | |
2510 if (*uj > MAX_ALLOWED_STRING_WIDTH) | |
2511 { | |
2512 format_overflow_error(pstart); | |
2513 return FAIL; | |
2514 } | |
2515 | |
2516 return OK; | |
2517 } | |
2518 | |
2519 | |
2471 static int | 2520 static int |
2472 parse_fmt_types( | 2521 parse_fmt_types( |
2473 const char ***ap_types, | 2522 const char ***ap_types, |
2474 int *num_posarg, | 2523 int *num_posarg, |
2475 const char *fmt, | 2524 const char *fmt, |
2509 char length_modifier = '\0'; | 2558 char length_modifier = '\0'; |
2510 | 2559 |
2511 // variable for positional arg | 2560 // variable for positional arg |
2512 int pos_arg = -1; | 2561 int pos_arg = -1; |
2513 const char *ptype = NULL; | 2562 const char *ptype = NULL; |
2563 const char *pstart = p+1; | |
2514 | 2564 |
2515 p++; // skip '%' | 2565 p++; // skip '%' |
2516 | 2566 |
2517 // First check to see if we find a positional | 2567 // First check to see if we find a positional |
2518 // argument specifier | 2568 // argument specifier |
2529 semsg(_( e_invalid_format_specifier_str), fmt); | 2579 semsg(_( e_invalid_format_specifier_str), fmt); |
2530 goto error; | 2580 goto error; |
2531 } | 2581 } |
2532 | 2582 |
2533 // Positional argument | 2583 // Positional argument |
2534 unsigned int uj = *p++ - '0'; | 2584 unsigned int uj; |
2535 | 2585 |
2536 while (VIM_ISDIGIT((int)(*p))) | 2586 if (get_unsigned_int(pstart, &p, &uj) == FAIL) |
2537 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 2587 goto error; |
2588 | |
2538 pos_arg = uj; | 2589 pos_arg = uj; |
2539 | 2590 |
2540 any_pos = 1; | 2591 any_pos = 1; |
2541 CHECK_POS_ARG; | 2592 CHECK_POS_ARG; |
2542 | 2593 |
2569 p++; | 2620 p++; |
2570 | 2621 |
2571 if (VIM_ISDIGIT((int)(*p))) | 2622 if (VIM_ISDIGIT((int)(*p))) |
2572 { | 2623 { |
2573 // Positional argument field width | 2624 // Positional argument field width |
2574 unsigned int uj = *p++ - '0'; | 2625 unsigned int uj; |
2575 | 2626 |
2576 while (VIM_ISDIGIT((int)(*p))) | 2627 if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) |
2577 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 2628 goto error; |
2578 | 2629 |
2579 if (*p != '$') | 2630 if (*p != '$') |
2580 { | 2631 { |
2581 semsg(_( e_invalid_format_specifier_str), fmt); | 2632 semsg(_( e_invalid_format_specifier_str), fmt); |
2582 goto error; | 2633 goto error; |
2599 } | 2650 } |
2600 else if (VIM_ISDIGIT((int)(*p))) | 2651 else if (VIM_ISDIGIT((int)(*p))) |
2601 { | 2652 { |
2602 // size_t could be wider than unsigned int; make sure we treat | 2653 // size_t could be wider than unsigned int; make sure we treat |
2603 // argument like common implementations do | 2654 // argument like common implementations do |
2604 unsigned int uj = *p++ - '0'; | 2655 const char *digstart = p; |
2605 | 2656 unsigned int uj; |
2606 while (VIM_ISDIGIT((int)(*p))) | 2657 |
2607 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 2658 if (get_unsigned_int(digstart, &p, &uj) == FAIL) |
2659 goto error; | |
2608 | 2660 |
2609 if (*p == '$') | 2661 if (*p == '$') |
2610 { | 2662 { |
2611 semsg(_( e_invalid_format_specifier_str), fmt); | 2663 semsg(_( e_invalid_format_specifier_str), fmt); |
2612 goto error; | 2664 goto error; |
2623 p++; | 2675 p++; |
2624 | 2676 |
2625 if (VIM_ISDIGIT((int)(*p))) | 2677 if (VIM_ISDIGIT((int)(*p))) |
2626 { | 2678 { |
2627 // Parse precision | 2679 // Parse precision |
2628 unsigned int uj = *p++ - '0'; | 2680 unsigned int uj; |
2629 | 2681 |
2630 while (VIM_ISDIGIT((int)(*p))) | 2682 if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) |
2631 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 2683 goto error; |
2632 | 2684 |
2633 if (*p == '$') | 2685 if (*p == '$') |
2634 { | 2686 { |
2635 any_pos = 1; | 2687 any_pos = 1; |
2636 CHECK_POS_ARG; | 2688 CHECK_POS_ARG; |
2654 } | 2706 } |
2655 else if (VIM_ISDIGIT((int)(*p))) | 2707 else if (VIM_ISDIGIT((int)(*p))) |
2656 { | 2708 { |
2657 // size_t could be wider than unsigned int; make sure we | 2709 // size_t could be wider than unsigned int; make sure we |
2658 // treat argument like common implementations do | 2710 // treat argument like common implementations do |
2659 unsigned int uj = *p++ - '0'; | 2711 const char *digstart = p; |
2660 | 2712 unsigned int uj; |
2661 while (VIM_ISDIGIT((int)(*p))) | 2713 |
2662 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 2714 if (get_unsigned_int(digstart, &p, &uj) == FAIL) |
2715 goto error; | |
2663 | 2716 |
2664 if (*p == '$') | 2717 if (*p == '$') |
2665 { | 2718 { |
2666 semsg(_( e_invalid_format_specifier_str), fmt); | 2719 semsg(_( e_invalid_format_specifier_str), fmt); |
2667 goto error; | 2720 goto error; |
2966 ++ptype; | 3019 ++ptype; |
2967 | 3020 |
2968 if (*ptype == '$') | 3021 if (*ptype == '$') |
2969 { | 3022 { |
2970 // Positional argument | 3023 // Positional argument |
2971 unsigned int uj = *p++ - '0'; | 3024 const char *digstart = p; |
2972 | 3025 unsigned int uj; |
2973 while (VIM_ISDIGIT((int)(*p))) | 3026 |
2974 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 3027 if (get_unsigned_int(digstart, &p, &uj) == FAIL) |
3028 goto error; | |
3029 | |
2975 pos_arg = uj; | 3030 pos_arg = uj; |
2976 | 3031 |
2977 ++p; | 3032 ++p; |
2978 } | 3033 } |
2979 | 3034 |
3000 | 3055 |
3001 // parse field width | 3056 // parse field width |
3002 if (*p == '*') | 3057 if (*p == '*') |
3003 { | 3058 { |
3004 int j; | 3059 int j; |
3060 const char *digstart = p + 1; | |
3005 | 3061 |
3006 p++; | 3062 p++; |
3007 | 3063 |
3008 if (VIM_ISDIGIT((int)(*p))) | 3064 if (VIM_ISDIGIT((int)(*p))) |
3009 { | 3065 { |
3010 // Positional argument field width | 3066 // Positional argument field width |
3011 unsigned int uj = *p++ - '0'; | 3067 unsigned int uj; |
3012 | 3068 |
3013 while (VIM_ISDIGIT((int)(*p))) | 3069 if (get_unsigned_int(digstart, &p, &uj) == FAIL) |
3014 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 3070 goto error; |
3071 | |
3015 arg_idx = uj; | 3072 arg_idx = uj; |
3016 | 3073 |
3017 ++p; | 3074 ++p; |
3018 } | 3075 } |
3019 | 3076 |
3023 # endif | 3080 # endif |
3024 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, | 3081 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, |
3025 &arg_cur, fmt), | 3082 &arg_cur, fmt), |
3026 va_arg(ap, int)); | 3083 va_arg(ap, int)); |
3027 | 3084 |
3085 if (j > MAX_ALLOWED_STRING_WIDTH) | |
3086 { | |
3087 format_overflow_error(digstart); | |
3088 goto error; | |
3089 } | |
3090 | |
3028 if (j >= 0) | 3091 if (j >= 0) |
3029 min_field_width = j; | 3092 min_field_width = j; |
3030 else | 3093 else |
3031 { | 3094 { |
3032 min_field_width = -j; | 3095 min_field_width = -j; |
3035 } | 3098 } |
3036 else if (VIM_ISDIGIT((int)(*p))) | 3099 else if (VIM_ISDIGIT((int)(*p))) |
3037 { | 3100 { |
3038 // size_t could be wider than unsigned int; make sure we treat | 3101 // size_t could be wider than unsigned int; make sure we treat |
3039 // argument like common implementations do | 3102 // argument like common implementations do |
3040 unsigned int uj = *p++ - '0'; | 3103 const char *digstart = p; |
3041 | 3104 unsigned int uj; |
3042 while (VIM_ISDIGIT((int)(*p))) | 3105 |
3043 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 3106 if (get_unsigned_int(digstart, &p, &uj) == FAIL) |
3107 goto error; | |
3108 | |
3109 if (uj > MAX_ALLOWED_STRING_WIDTH) | |
3110 { | |
3111 format_overflow_error(digstart); | |
3112 goto error; | |
3113 } | |
3114 | |
3044 min_field_width = uj; | 3115 min_field_width = uj; |
3045 } | 3116 } |
3046 | 3117 |
3047 // parse precision | 3118 // parse precision |
3048 if (*p == '.') | 3119 if (*p == '.') |
3052 | 3123 |
3053 if (VIM_ISDIGIT((int)(*p))) | 3124 if (VIM_ISDIGIT((int)(*p))) |
3054 { | 3125 { |
3055 // size_t could be wider than unsigned int; make sure we | 3126 // size_t could be wider than unsigned int; make sure we |
3056 // treat argument like common implementations do | 3127 // treat argument like common implementations do |
3057 unsigned int uj = *p++ - '0'; | 3128 const char *digstart = p; |
3058 | 3129 unsigned int uj; |
3059 while (VIM_ISDIGIT((int)(*p))) | 3130 |
3060 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 3131 if (get_unsigned_int(digstart, &p, &uj) == FAIL) |
3132 goto error; | |
3133 | |
3134 if (uj > MAX_ALLOWED_STRING_WIDTH) | |
3135 { | |
3136 format_overflow_error(digstart); | |
3137 goto error; | |
3138 } | |
3139 | |
3061 precision = uj; | 3140 precision = uj; |
3062 } | 3141 } |
3063 else if (*p == '*') | 3142 else if (*p == '*') |
3064 { | 3143 { |
3065 int j; | 3144 int j; |
3145 const char *digstart = p; | |
3066 | 3146 |
3067 p++; | 3147 p++; |
3068 | 3148 |
3069 if (VIM_ISDIGIT((int)(*p))) | 3149 if (VIM_ISDIGIT((int)(*p))) |
3070 { | 3150 { |
3071 // positional argument | 3151 // positional argument |
3072 unsigned int uj = *p++ - '0'; | 3152 unsigned int uj; |
3073 | 3153 |
3074 while (VIM_ISDIGIT((int)(*p))) | 3154 if (get_unsigned_int(digstart, &p, &uj) == FAIL) |
3075 uj = 10 * uj + (unsigned int)(*p++ - '0'); | 3155 goto error; |
3156 | |
3076 arg_idx = uj; | 3157 arg_idx = uj; |
3077 | 3158 |
3078 ++p; | 3159 ++p; |
3079 } | 3160 } |
3080 | 3161 |
3083 tvs != NULL ? tv_nr(tvs, &arg_idx) : | 3164 tvs != NULL ? tv_nr(tvs, &arg_idx) : |
3084 # endif | 3165 # endif |
3085 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, | 3166 (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, |
3086 &arg_cur, fmt), | 3167 &arg_cur, fmt), |
3087 va_arg(ap, int)); | 3168 va_arg(ap, int)); |
3169 | |
3170 if (j > MAX_ALLOWED_STRING_WIDTH) | |
3171 { | |
3172 format_overflow_error(digstart); | |
3173 goto error; | |
3174 } | |
3088 | 3175 |
3089 if (j >= 0) | 3176 if (j >= 0) |
3090 precision = j; | 3177 precision = j; |
3091 else | 3178 else |
3092 { | 3179 { |
3871 } | 3958 } |
3872 | 3959 |
3873 if (tvs != NULL && tvs[num_posarg != 0 ? num_posarg : arg_idx - 1].v_type != VAR_UNKNOWN) | 3960 if (tvs != NULL && tvs[num_posarg != 0 ? num_posarg : arg_idx - 1].v_type != VAR_UNKNOWN) |
3874 emsg(_(e_too_many_arguments_to_printf)); | 3961 emsg(_(e_too_many_arguments_to_printf)); |
3875 | 3962 |
3963 error: | |
3876 vim_free((char*)ap_types); | 3964 vim_free((char*)ap_types); |
3877 va_end(ap); | 3965 va_end(ap); |
3878 | 3966 |
3879 // Return the number of characters formatted (excluding trailing nul | 3967 // Return the number of characters formatted (excluding trailing nul |
3880 // character), that is, the number of characters that would have been | 3968 // character), that is, the number of characters that would have been |