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