commit c81f7aeee2d861551f4735fbfd1429fcf5202020
parent 444e1ffe3e9c093d764eeda4d469abe46c6c1565
Author: Jan Edmund Lazo <jan.lazo@mail.utoronto.ca>
Date: Sun, 28 Dec 2025 01:26:29 -0500
vim-patch:8.2.0098: exe stack length can be wrong without being detected (#37136)
Problem: Exe stack length can be wrong without being detected.
Solution: Add a check when ABORT_ON_INTERNAL_ERROR is defined.
https://github.com/vim/vim/commit/e31ee86859528a7ffe00405645547d494e522fa8
vim-patch:8.2.3262: build failure when ABORT_ON_INTERNAL_ERROR is defined
Port patch 9.0.1454 for "make formatc".
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Diffstat:
8 files changed, 43 insertions(+), 0 deletions(-)
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
@@ -1669,6 +1669,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
bool did_save_redobuff = false;
save_redo_T save_redo;
const bool save_KeyTyped = KeyTyped;
+ ESTACK_CHECK_DECLARATION;
// Quickly return if there are no autocommands for this event or
// autocommands are blocked.
@@ -1853,6 +1854,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// name and lnum are filled in later
estack_push(ETYPE_AUCMD, NULL, 0);
+ ESTACK_CHECK_SETUP;
const sctx_T save_current_sctx = current_sctx;
@@ -1962,6 +1964,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
filechangeshell_busy = false;
autocmd_nested = save_autocmd_nested;
xfree(SOURCING_NAME);
+ ESTACK_CHECK_NOW;
estack_pop();
xfree(afile_orig);
xfree(autocmd_fname);
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
@@ -3788,6 +3788,7 @@ static int chk_modeline(linenr_T lnum, int flags)
{
char *e;
int retval = OK;
+ ESTACK_CHECK_DECLARATION;
int prev = -1;
char *s = ml_get(lnum);
@@ -3843,6 +3844,7 @@ static int chk_modeline(linenr_T lnum, int flags)
// prepare for emsg()
estack_push(ETYPE_MODELINE, "modelines", lnum);
+ ESTACK_CHECK_SETUP;
bool end = false;
while (end == false) {
@@ -3899,6 +3901,7 @@ static int chk_modeline(linenr_T lnum, int flags)
// careful not to go off the end
}
+ ESTACK_CHECK_NOW;
estack_pop();
xfree(linecopy);
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
@@ -1010,6 +1010,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool started_profiling = false;
bool did_save_redo = false;
save_redo_T save_redo;
+ ESTACK_CHECK_DECLARATION;
// If depth of calling is getting too high, don't execute the function
if (depth >= p_mfd) {
@@ -1181,6 +1182,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
}
estack_push_ufunc(fp, 1);
+ ESTACK_CHECK_SETUP;
if (p_verbose >= 12) {
no_wait_return++;
verbose_enter_scroll();
@@ -1332,6 +1334,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
no_wait_return--;
}
+ ESTACK_CHECK_NOW;
estack_pop();
current_sctx = save_current_sctx;
if (do_profiling_yes) {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
@@ -964,6 +964,7 @@ void handle_did_throw(void)
assert(current_exception != NULL);
char *p = NULL;
msglist_T *messages = NULL;
+ ESTACK_CHECK_DECLARATION;
// If the uncaught exception is a user exception, report it as an
// error. If it is an error exception, display the saved error
@@ -985,6 +986,7 @@ void handle_did_throw(void)
}
estack_push(ETYPE_EXCEPT, current_exception->throw_name, current_exception->throw_lnum);
+ ESTACK_CHECK_SETUP;
current_exception->throw_name = NULL;
discard_current_exception(); // uses IObuff if 'verbose'
@@ -1009,6 +1011,7 @@ void handle_did_throw(void)
xfree(p);
}
xfree(SOURCING_NAME);
+ ESTACK_CHECK_NOW;
estack_pop();
}
diff --git a/src/nvim/main.c b/src/nvim/main.c
@@ -1918,6 +1918,7 @@ static void exe_pre_commands(mparm_T *parmp)
{
char **cmds = parmp->pre_commands;
int cnt = parmp->n_pre_commands;
+ ESTACK_CHECK_DECLARATION;
if (cnt <= 0) {
return;
@@ -1925,10 +1926,12 @@ static void exe_pre_commands(mparm_T *parmp)
curwin->w_cursor.lnum = 0; // just in case..
estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0);
+ ESTACK_CHECK_SETUP;
current_sctx.sc_sid = SID_CMDARG;
for (int i = 0; i < cnt; i++) {
do_cmdline_cmd(cmds[i]);
}
+ ESTACK_CHECK_NOW;
estack_pop();
current_sctx.sc_sid = 0;
TIME_MSG("--cmd commands");
@@ -1937,6 +1940,8 @@ static void exe_pre_commands(mparm_T *parmp)
// Execute "+", "-c" and "-S" arguments.
static void exe_commands(mparm_T *parmp)
{
+ ESTACK_CHECK_DECLARATION;
+
// We start commands on line 0, make "vim +/pat file" match a
// pattern on line 1. But don't move the cursor when an autocommand
// with g`" was used.
@@ -1945,6 +1950,7 @@ static void exe_commands(mparm_T *parmp)
curwin->w_cursor.lnum = 0;
}
estack_push(ETYPE_ARGS, "command line", 0);
+ ESTACK_CHECK_SETUP;
current_sctx.sc_sid = SID_CARG;
current_sctx.sc_seq = 0;
for (int i = 0; i < parmp->n_commands; i++) {
@@ -1953,6 +1959,7 @@ static void exe_commands(mparm_T *parmp)
xfree(parmp->commands[i]);
}
}
+ ESTACK_CHECK_NOW;
estack_pop();
current_sctx.sc_sid = 0;
if (curwin->w_cursor.lnum == 0) {
@@ -2149,18 +2156,21 @@ static void source_startup_scripts(const mparm_T *const parmp)
static int execute_env(char *env)
FUNC_ATTR_NONNULL_ALL
{
+ ESTACK_CHECK_DECLARATION;
char *initstr = os_getenv(env);
if (initstr == NULL) {
return FAIL;
}
estack_push(ETYPE_ENV, env, 0);
+ ESTACK_CHECK_SETUP;
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_ENV;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
do_cmdline_cmd(initstr);
+ ESTACK_CHECK_NOW;
estack_pop();
current_sctx = save_current_sctx;
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
@@ -2092,6 +2092,7 @@ static int do_source_ext(char *const fname, const bool check_other, const int is
scriptitem_T *si = NULL;
proftime_T wait_start;
bool trigger_source_post = false;
+ ESTACK_CHECK_DECLARATION;
CLEAR_FIELD(cookie);
char *fname_exp = NULL;
@@ -2255,6 +2256,7 @@ static int do_source_ext(char *const fname, const bool check_other, const int is
// Keep the sourcing name/lnum, for recursive calls.
estack_push(ETYPE_SCRIPT, si != NULL ? si->sn_name : fname_exp, 0);
+ ESTACK_CHECK_SETUP;
if (l_do_profiling == PROF_YES && si != NULL) {
bool forceit = false;
@@ -2316,6 +2318,7 @@ static int do_source_ext(char *const fname, const bool check_other, const int is
if (got_int) {
emsg(_(e_interr));
}
+ ESTACK_CHECK_NOW;
estack_pop();
if (p_verbose > 1) {
verbose_enter();
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
@@ -45,4 +45,19 @@ enum {
DIP_DIRFILE = 0x200, ///< find both files and directories
};
+#ifdef ABORT_ON_INTERNAL_ERROR
+# define ESTACK_CHECK_DECLARATION int estack_len_before
+# define ESTACK_CHECK_SETUP do { estack_len_before = exestack.ga_len; } while (0)
+# define ESTACK_CHECK_NOW \
+ do { \
+ if (estack_len_before != exestack.ga_len) { \
+ siemsg("Exestack length expected: %d, actual: %d", estack_len_before, exestack.ga_len); \
+ } \
+ } while (0)
+#else
+# define ESTACK_CHECK_DECLARATION do {} while (0)
+# define ESTACK_CHECK_SETUP do {} while (0)
+# define ESTACK_CHECK_NOW do {} while (0)
+#endif
+
#include "runtime.h.generated.h"
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
@@ -608,6 +608,7 @@ slang_T *spell_load_file(char *fname, char *lang, slang_T *old_lp, bool silent)
slang_T *lp = NULL;
int res;
bool did_estack_push = false;
+ ESTACK_CHECK_DECLARATION;
FILE *fd = os_fopen(fname, "r");
if (fd == NULL) {
@@ -640,6 +641,7 @@ slang_T *spell_load_file(char *fname, char *lang, slang_T *old_lp, bool silent)
// Set sourcing_name, so that error messages mention the file name.
estack_push(ETYPE_SPELL, fname, 0);
+ ESTACK_CHECK_SETUP;
did_estack_push = true;
// <HEADER>: <fileID>
@@ -838,6 +840,7 @@ endOK:
fclose(fd);
}
if (did_estack_push) {
+ ESTACK_CHECK_NOW;
estack_pop();
}