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.
This commit is contained in:
Sean Dewar
2026-02-15 00:13:37 +00:00
parent f8d59cfab9
commit 1349233cd1
3 changed files with 23 additions and 11 deletions

View File

@@ -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).

View File

@@ -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];

View File

@@ -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)