Mercurial > vim
comparison src/vim9execute.c @ 32339:2e07c2bb2f60 v9.0.1501
patch 9.0.1501: crash with nested :try and :throw in catch block
Commit: https://github.com/vim/vim/commit/3ef2e41128bbe372f4e4535429ffa4770f572623
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Apr 30 18:50:48 2023 +0100
patch 9.0.1501: crash with nested :try and :throw in catch block
Problem: Crash with nested :try and :throw in catch block.
Solution: Jump to :endtry before returning from function. (closes https://github.com/vim/vim/issues/12245)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 30 Apr 2023 20:00:04 +0200 |
parents | 83caf07aedd6 |
children | 933cec0b89dc |
comparison
equal
deleted
inserted
replaced
32338:612a261c3743 | 32339:2e07c2bb2f60 |
---|---|
3051 did_emsg = FALSE; | 3051 did_emsg = FALSE; |
3052 if (throw_exception(*msg_list, ET_ERROR, NULL) == FAIL) | 3052 if (throw_exception(*msg_list, ET_ERROR, NULL) == FAIL) |
3053 goto theend; | 3053 goto theend; |
3054 did_throw = TRUE; | 3054 did_throw = TRUE; |
3055 *msg_list = NULL; | 3055 *msg_list = NULL; |
3056 | |
3057 // This exception was not caught (yet). | |
3058 garray_T *trystack = &ectx->ec_trystack; | |
3059 if (trystack->ga_len > 0) | |
3060 { | |
3061 trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data) | |
3062 + trystack->ga_len - 1; | |
3063 if (trycmd->tcd_frame_idx == ectx->ec_frame_idx) | |
3064 trycmd->tcd_caught = FALSE; | |
3065 } | |
3056 } | 3066 } |
3057 | 3067 |
3058 if (unlikely(did_throw)) | 3068 if (unlikely(did_throw)) |
3059 { | 3069 { |
3060 garray_T *trystack = &ectx->ec_trystack; | 3070 garray_T *trystack = &ectx->ec_trystack; |
3064 // An exception jumps to the first catch, finally, or returns from | 3074 // An exception jumps to the first catch, finally, or returns from |
3065 // the current function. | 3075 // the current function. |
3066 while (index > 0) | 3076 while (index > 0) |
3067 { | 3077 { |
3068 trycmd = ((trycmd_T *)trystack->ga_data) + index - 1; | 3078 trycmd = ((trycmd_T *)trystack->ga_data) + index - 1; |
3069 if (!trycmd->tcd_in_catch || trycmd->tcd_finally_idx != 0) | 3079 // 1. after :try and before :catch - jump to first :catch |
3080 // 2. in :catch block - jump to :finally | |
3081 // 3. in :catch block and no finally - jump to :endtry | |
3082 if (!trycmd->tcd_in_catch || trycmd->tcd_finally_idx != 0 | |
3083 || trycmd->tcd_frame_idx == ectx->ec_frame_idx) | |
3070 break; | 3084 break; |
3071 // In the catch and finally block of this try we have to go up | 3085 // In the catch and finally block of this try we have to go up |
3072 // one level. | 3086 // one level. |
3073 --index; | 3087 --index; |
3074 trycmd = NULL; | 3088 trycmd = NULL; |
3075 } | 3089 } |
3076 if (trycmd != NULL && trycmd->tcd_frame_idx == ectx->ec_frame_idx) | 3090 if (trycmd != NULL && trycmd->tcd_frame_idx == ectx->ec_frame_idx) |
3077 { | 3091 { |
3078 if (trycmd->tcd_in_catch) | 3092 if (trycmd->tcd_in_catch) |
3079 { | 3093 { |
3080 // exception inside ":catch", jump to ":finally" once | 3094 if (trycmd->tcd_finally_idx > 0) |
3081 ectx->ec_iidx = trycmd->tcd_finally_idx; | 3095 { |
3082 trycmd->tcd_finally_idx = 0; | 3096 // exception inside ":catch", jump to ":finally" once |
3097 ectx->ec_iidx = trycmd->tcd_finally_idx; | |
3098 trycmd->tcd_finally_idx = 0; | |
3099 } | |
3100 else | |
3101 { | |
3102 // exception inside ":catch" or ":finally", jump to | |
3103 // ":endtry" | |
3104 ectx->ec_iidx = trycmd->tcd_endtry_idx; | |
3105 } | |
3083 } | 3106 } |
3084 else | 3107 else |
3108 { | |
3085 // jump to first ":catch" | 3109 // jump to first ":catch" |
3086 ectx->ec_iidx = trycmd->tcd_catch_idx; | 3110 ectx->ec_iidx = trycmd->tcd_catch_idx; |
3087 trycmd->tcd_in_catch = TRUE; | 3111 trycmd->tcd_in_catch = TRUE; |
3112 } | |
3088 did_throw = FALSE; // don't come back here until :endtry | 3113 did_throw = FALSE; // don't come back here until :endtry |
3089 trycmd->tcd_did_throw = TRUE; | 3114 trycmd->tcd_did_throw = TRUE; |
3090 } | 3115 } |
3091 else | 3116 else |
3092 { | 3117 { |
3093 // Not inside try or need to return from current functions. | 3118 // Not inside try or need to return from current function. |
3094 // Push a dummy return value. | 3119 // Push a dummy return value. |
3095 if (GA_GROW_FAILS(&ectx->ec_stack, 1)) | 3120 if (GA_GROW_FAILS(&ectx->ec_stack, 1)) |
3096 goto theend; | 3121 goto theend; |
3097 tv = STACK_TV_BOT(0); | 3122 tv = STACK_TV_BOT(0); |
3098 tv->v_type = VAR_NUMBER; | 3123 tv->v_type = VAR_NUMBER; |
4650 { | 4675 { |
4651 garray_T *trystack = &ectx->ec_trystack; | 4676 garray_T *trystack = &ectx->ec_trystack; |
4652 | 4677 |
4653 if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent) | 4678 if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent) |
4654 { | 4679 { |
4655 // throwing an exception while using "silent!" causes | 4680 // Throwing an exception while using "silent!" causes |
4656 // the function to abort but not display an error. | 4681 // the function to abort but not display an error. |
4657 tv = STACK_TV_BOT(-1); | 4682 tv = STACK_TV_BOT(-1); |
4658 clear_tv(tv); | 4683 clear_tv(tv); |
4659 tv->v_type = VAR_NUMBER; | 4684 tv->v_type = VAR_NUMBER; |
4660 tv->vval.v_number = 0; | 4685 tv->vval.v_number = 0; |