commit 1349233cd150dd904c38547371a24765d9217454
parent f8d59cfab9585a538760e404789029be743b61dc
Author: Sean Dewar <6256228+seandewar@users.noreply.github.com>
Date: Sun, 15 Feb 2026 00:13:37 +0000
fix(prompt): heap-buffer-overflow in prompt_setprompt
Problem: prompt_setprompt may check the wrong buffer, which can lead to a
heap-buffer-overflow.
Solution: don't use curbuf.
Also replace all kCallbackNone initializers with CALLBACK_INIT.
Diffstat:
3 files changed, 23 insertions(+), 11 deletions(-)
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
@@ -712,7 +712,7 @@ void restore_buffer(bufref_T *save_curbuf)
/// "prompt_setcallback({buffer}, {callback})" function
void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- Callback prompt_callback = { .type = kCallbackNone };
+ Callback prompt_callback = CALLBACK_INIT;
if (check_secure()) {
return;
@@ -735,7 +735,7 @@ void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "prompt_setinterrupt({buffer}, {callback})" function
void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- Callback interrupt_callback = { .type = kCallbackNone };
+ Callback interrupt_callback = CALLBACK_INIT;
if (check_secure()) {
return;
@@ -772,10 +772,8 @@ void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// Update the prompt-text and prompt-marks if a plugin calls prompt_setprompt()
// even while user is editing their input.
if (bt_prompt(buf)) {
- if (buf->b_prompt_start.mark.lnum > buf->b_ml.ml_line_count) {
- // In case the mark is set to a nonexistent line.
- buf->b_prompt_start.mark.lnum = buf->b_ml.ml_line_count;
- }
+ // In case the mark is set to a nonexistent line.
+ buf->b_prompt_start.mark.lnum = MIN(buf->b_prompt_start.mark.lnum, buf->b_ml.ml_line_count);
linenr_T prompt_lno = buf->b_prompt_start.mark.lnum;
char *old_prompt = buf_prompt_text(buf);
@@ -786,8 +784,7 @@ void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
colnr_T cursor_col = curwin->w_cursor.col;
if (buf->b_prompt_start.mark.col < old_prompt_len
- || curbuf->b_prompt_start.mark.col < old_prompt_len
- || !strnequal(old_prompt, old_line + curbuf->b_prompt_start.mark.col - old_prompt_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,
// replace prompt line with new-prompt (discards user-input).
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
@@ -4883,7 +4883,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
typval_T *cancelreturn = NULL;
typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;
const char *xp_name = NULL;
- Callback input_callback = { .type = kCallbackNone };
+ Callback input_callback = CALLBACK_INIT;
char prompt_buf[NUMBUFLEN];
char defstr_buf[NUMBUFLEN];
char cancelreturn_buf[NUMBUFLEN];
diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua
@@ -798,8 +798,8 @@ describe('prompt buffer', function()
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
local buf = api.nvim_get_current_buf()
- local function set_prompt(prompt)
- fn('prompt_setprompt', buf, prompt)
+ local function set_prompt(prompt, b)
+ fn('prompt_setprompt', b or buf, prompt)
end
set_prompt('> ')
@@ -846,5 +846,20 @@ describe('prompt buffer', function()
{5:-- INSERT --} |
]])
eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':'))
+
+ -- No crash when setting shorter prompt than curbuf's in other buffer.
+ command('new | setlocal buftype=prompt')
+ set_prompt('looooooooooooooooooooooooooooooooooooooooooooong > ', '') -- curbuf
+ set_prompt('foo > ')
+ screen:expect([[
+ loooooooooooooooooooooooo|
+ ooooooooooooooooooooong >|
+ ^ |
+ {1:~ }|
+ {3:[Prompt] [+] }|
+ foo > user input |
+ {1:~ }|*3
+ {5:-- INSERT --} |
+ ]])
end)
end)