Mercurial > vim
comparison src/userfunc.c @ 26747:a8a4e1e7b111 v8.2.3902
patch 8.2.3902: Vim9: double free with nested :def function
Commit: https://github.com/vim/vim/commit/9c23f9bb5fe435b28245ba8ac65aa0ca6b902c04
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Dec 26 14:23:22 2021 +0000
patch 8.2.3902: Vim9: double free with nested :def function
Problem: Vim9: double free with nested :def function.
Solution: Pass "line_to_free" from compile_def_function() and make sure
cmdlinep is valid.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 26 Dec 2021 15:30:02 +0100 |
parents | 9c9b8d95b05f |
children | 3a2b222107a6 |
comparison
equal
deleted
inserted
replaced
26746:57551c0135af | 26747:a8a4e1e7b111 |
---|---|
718 line_arg = p + 1; | 718 line_arg = p + 1; |
719 } | 719 } |
720 } | 720 } |
721 else | 721 else |
722 { | 722 { |
723 vim_free(*line_to_free); | |
724 if (eap->getline == NULL) | 723 if (eap->getline == NULL) |
725 theline = getcmdline(':', 0L, indent, getline_options); | 724 theline = getcmdline(':', 0L, indent, getline_options); |
726 else | 725 else |
727 theline = eap->getline(':', eap->cookie, indent, | 726 theline = eap->getline(':', eap->cookie, indent, |
728 getline_options); | 727 getline_options); |
728 if (*eap->cmdlinep == *line_to_free) | |
729 *eap->cmdlinep = theline; | |
730 vim_free(*line_to_free); | |
729 *line_to_free = theline; | 731 *line_to_free = theline; |
730 } | 732 } |
731 if (KeyTyped) | 733 if (KeyTyped) |
732 lines_left = Rows - 1; | 734 lines_left = Rows - 1; |
733 if (theline == NULL) | 735 if (theline == NULL) |
835 { | 837 { |
836 // Another command follows. If the line came from "eap" | 838 // Another command follows. If the line came from "eap" |
837 // we can simply point into it, otherwise we need to | 839 // we can simply point into it, otherwise we need to |
838 // change "eap->cmdlinep". | 840 // change "eap->cmdlinep". |
839 eap->nextcmd = nextcmd; | 841 eap->nextcmd = nextcmd; |
840 if (*line_to_free != NULL) | 842 if (*line_to_free != NULL |
843 && *eap->cmdlinep != *line_to_free) | |
841 { | 844 { |
842 vim_free(*eap->cmdlinep); | 845 vim_free(*eap->cmdlinep); |
843 *eap->cmdlinep = *line_to_free; | 846 *eap->cmdlinep = *line_to_free; |
844 *line_to_free = NULL; | 847 *line_to_free = NULL; |
845 } | 848 } |
1159 ((char_u **)gap->ga_data)[gap->ga_len++] = pnl; | 1162 ((char_u **)gap->ga_data)[gap->ga_len++] = pnl; |
1160 ((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl; | 1163 ((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl; |
1161 } | 1164 } |
1162 if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL) | 1165 if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL) |
1163 goto erret; | 1166 goto erret; |
1164 if (cmdline != NULL) | 1167 if (eap.nextcmd != NULL) |
1165 // more is following after the "}", which was skipped | 1168 // more is following after the "}", which was skipped |
1166 last = cmdline; | 1169 last = cmdline; |
1167 else | 1170 else |
1168 // nothing is following the "}" | 1171 // nothing is following the "}" |
1169 last = (char_u *)"}"; | 1172 last = (char_u *)"}"; |
1173 mch_memmove(pnl + 1, last, plen + 1); | 1176 mch_memmove(pnl + 1, last, plen + 1); |
1174 ((char_u **)gap->ga_data)[gap->ga_len++] = pnl; | 1177 ((char_u **)gap->ga_data)[gap->ga_len++] = pnl; |
1175 ((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl; | 1178 ((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl; |
1176 } | 1179 } |
1177 | 1180 |
1178 if (cmdline != NULL) | 1181 if (eap.nextcmd != NULL) |
1179 { | 1182 { |
1180 garray_T *tfgap = &evalarg->eval_tofree_ga; | 1183 garray_T *tfgap = &evalarg->eval_tofree_ga; |
1181 | 1184 |
1182 // Something comes after the "}". | 1185 // Something comes after the "}". |
1183 *arg = eap.nextcmd; | 1186 *arg = eap.nextcmd; |
1185 // "arg" points into cmdline, need to keep the line and free it later. | 1188 // "arg" points into cmdline, need to keep the line and free it later. |
1186 if (ga_grow(tfgap, 1) == OK) | 1189 if (ga_grow(tfgap, 1) == OK) |
1187 { | 1190 { |
1188 ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline; | 1191 ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline; |
1189 evalarg->eval_using_cmdline = TRUE; | 1192 evalarg->eval_using_cmdline = TRUE; |
1193 if (cmdline == line_to_free) | |
1194 line_to_free = NULL; | |
1190 } | 1195 } |
1191 } | 1196 } |
1192 else | 1197 else |
1193 *arg = (char_u *)""; | 1198 *arg = (char_u *)""; |
1194 | 1199 |
3986 * When "name_arg" is not NULL this is a nested function, using "name_arg" for | 3991 * When "name_arg" is not NULL this is a nested function, using "name_arg" for |
3987 * the function name. | 3992 * the function name. |
3988 * Returns a pointer to the function or NULL if no function defined. | 3993 * Returns a pointer to the function or NULL if no function defined. |
3989 */ | 3994 */ |
3990 ufunc_T * | 3995 ufunc_T * |
3991 define_function(exarg_T *eap, char_u *name_arg) | 3996 define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free) |
3992 { | 3997 { |
3993 char_u *line_to_free = NULL; | |
3994 int j; | 3998 int j; |
3995 int c; | 3999 int c; |
3996 int saved_did_emsg; | 4000 int saved_did_emsg; |
3997 char_u *name = name_arg; | 4001 char_u *name = name_arg; |
3998 int is_global = FALSE; | 4002 int is_global = FALSE; |
4256 // invalid. | 4260 // invalid. |
4257 ++p; | 4261 ++p; |
4258 if (get_function_args(&p, ')', &newargs, | 4262 if (get_function_args(&p, ')', &newargs, |
4259 eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, | 4263 eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, |
4260 NULL, &varargs, &default_args, eap->skip, | 4264 NULL, &varargs, &default_args, eap->skip, |
4261 eap, &line_to_free) == FAIL) | 4265 eap, line_to_free) == FAIL) |
4262 goto errret_2; | 4266 goto errret_2; |
4263 whitep = p; | 4267 whitep = p; |
4264 | 4268 |
4265 if (eap->cmdidx == CMD_def) | 4269 if (eap->cmdidx == CMD_def) |
4266 { | 4270 { |
4366 // Save the starting line number. | 4370 // Save the starting line number. |
4367 sourcing_lnum_top = SOURCING_LNUM; | 4371 sourcing_lnum_top = SOURCING_LNUM; |
4368 | 4372 |
4369 // Do not define the function when getting the body fails and when | 4373 // Do not define the function when getting the body fails and when |
4370 // skipping. | 4374 // skipping. |
4371 if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL | 4375 if (get_function_body(eap, &newlines, line_arg, line_to_free) == FAIL |
4372 || eap->skip) | 4376 || eap->skip) |
4373 goto erret; | 4377 goto erret; |
4374 | 4378 |
4375 /* | 4379 /* |
4376 * If there are no errors, add the function | 4380 * If there are no errors, add the function |
4658 vim_free(fp); | 4662 vim_free(fp); |
4659 fp = NULL; | 4663 fp = NULL; |
4660 } | 4664 } |
4661 ret_free: | 4665 ret_free: |
4662 ga_clear_strings(&argtypes); | 4666 ga_clear_strings(&argtypes); |
4663 vim_free(line_to_free); | |
4664 vim_free(fudi.fd_newkey); | 4667 vim_free(fudi.fd_newkey); |
4665 if (name != name_arg) | 4668 if (name != name_arg) |
4666 vim_free(name); | 4669 vim_free(name); |
4667 vim_free(ret_type); | 4670 vim_free(ret_type); |
4668 did_emsg |= saved_did_emsg; | 4671 did_emsg |= saved_did_emsg; |
4674 * ":function" | 4677 * ":function" |
4675 */ | 4678 */ |
4676 void | 4679 void |
4677 ex_function(exarg_T *eap) | 4680 ex_function(exarg_T *eap) |
4678 { | 4681 { |
4679 (void)define_function(eap, NULL); | 4682 char_u *line_to_free = NULL; |
4683 | |
4684 (void)define_function(eap, NULL, &line_to_free); | |
4685 vim_free(line_to_free); | |
4680 } | 4686 } |
4681 | 4687 |
4682 /* | 4688 /* |
4683 * :defcompile - compile all :def functions in the current script that need to | 4689 * :defcompile - compile all :def functions in the current script that need to |
4684 * be compiled. Except dead functions. Doesn't do profiling. | 4690 * be compiled. Except dead functions. Doesn't do profiling. |