From 4afbc25432820be65fc304717b2e8f450c92ff62 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sun, 15 Feb 2026 01:01:41 +0000 Subject: [PATCH] fix(prompt): heap-buffer-overflows with invalid ': col Problem: heap-buffer-overflow in init_prompt and prompt_setprompt if ': mark has an invalid column number. Solution: consider an out-of-bounds column number as a missing prompt. Remove the check for NULL for old_line, as ml_get_buf can't return NULL. --- src/nvim/edit.c | 7 ++++--- src/nvim/eval/buffer.c | 3 ++- test/functional/legacy/prompt_buffer_spec.lua | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 956394db1b..533fa95e99 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1593,12 +1593,13 @@ static void init_prompt(int cmdchar_todo) char *prompt = prompt_text(); int prompt_len = (int)strlen(prompt); - if (curwin->w_cursor.lnum < curbuf->b_prompt_start.mark.lnum) { - curwin->w_cursor.lnum = curbuf->b_prompt_start.mark.lnum; - } + curwin->w_cursor.lnum = MAX(curwin->w_cursor.lnum, curbuf->b_prompt_start.mark.lnum); char *text = ml_get(curbuf->b_prompt_start.mark.lnum); + colnr_T text_len = ml_get_len(curbuf->b_prompt_start.mark.lnum); + if ((curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum && (curbuf->b_prompt_start.mark.col < prompt_len + || curbuf->b_prompt_start.mark.col > text_len || !strnequal(text + curbuf->b_prompt_start.mark.col - prompt_len, prompt, (size_t)prompt_len)))) { // prompt is missing, insert it or append a line with it diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c index 117acdcf5e..813d839dfe 100644 --- a/src/nvim/eval/buffer.c +++ b/src/nvim/eval/buffer.c @@ -778,12 +778,13 @@ void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) linenr_T prompt_lno = buf->b_prompt_start.mark.lnum; char *old_prompt = buf_prompt_text(buf); char *old_line = ml_get_buf(buf, prompt_lno); - old_line = old_line != NULL ? old_line : ""; + colnr_T old_line_len = ml_get_buf_len(buf, prompt_lno); int old_prompt_len = (int)strlen(old_prompt); colnr_T cursor_col = curwin->w_cursor.col; if (buf->b_prompt_start.mark.col < old_prompt_len + || buf->b_prompt_start.mark.col > old_line_len || !strnequal(old_prompt, old_line + buf->b_prompt_start.mark.col - old_prompt_len, (size_t)old_prompt_len)) { // If for some odd reason the old prompt is missing, diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua index 83825c8a94..1acc6a2258 100644 --- a/test/functional/legacy/prompt_buffer_spec.lua +++ b/test/functional/legacy/prompt_buffer_spec.lua @@ -667,6 +667,10 @@ describe('prompt buffer', function() eq({ last_line, 6 }, api.nvim_buf_get_mark(0, ':')) eq(true, api.nvim_buf_set_mark(0, ':', 1, 5, {})) eq({ 1, 5 }, api.nvim_buf_get_mark(0, ':')) + + -- No crash from invalid col. + eq(true, api.nvim_buf_set_mark(0, ':', fn('line', '.'), 999, {})) + eq({ 12, 6 }, api.nvim_buf_get_mark(0, ':')) end) describe('prompt_getinput', function() @@ -861,5 +865,17 @@ describe('prompt buffer', function() {1:~ }|*3 {5:-- INSERT --} | ]]) + + -- No prompt_setprompt crash from invalid ': col. Must happen in the same event. + exec_lua(function() + vim.cmd 'bwipeout!' + vim.api.nvim_buf_set_mark(0, ':', vim.fn.line('.'), 999, {}) + vim.fn.prompt_setprompt('', 'new-prompt > ') + end) + screen:expect([[ + new-prompt > ^ | + {1:~ }|*8 + {5:-- INSERT --} | + ]]) end) end)