neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 9e79f7433eb0e8c5ab9b7c84ac7670aac2f56671
parent ebfb6399d957f893fd3943cd1f495dbc83a44d47
Author: Andreas Schneider <asn@cryptomilk.org>
Date:   Fri, 21 Apr 2023 13:16:32 +0200

fix(usercmd): Fix buffer overflow in uc_list() (#23225)

fix(usercmd): fix buffer overflow in uc_list()

Build with: -Wp,-D_FORTIFY_SOURCE=3 -O1 and gcc 13.

*** buffer overflow detected ***: terminated

(gdb) bt
  #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
  #1  0x00007f3eb8b93c03 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
  #2  0x00007f3eb8b42aee in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
  #3  0x00007f3eb8b2b87f in __GI_abort () at abort.c:79
  #4  0x00007f3eb8b2c60f in __libc_message (fmt=fmt@entry=0x7f3eb8ca72e6 "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:150
  #5  0x00007f3eb8c27b29 in __GI___fortify_fail (msg=msg@entry=0x7f3eb8ca728c "buffer overflow detected") at fortify_fail.c:24
  #6  0x00007f3eb8c26364 in __GI___chk_fail () at chk_fail.c:28
  #7  0x00007f3eb8c25f45 in ___snprintf_chk (s=s@entry=0x55b8c7c096a5 <IObuff+5> "t' item", maxlen=maxlen@entry=1025, flag=flag@entry=2, slen=slen@entry=1020, format=format@entry=0x55b8c7b872a6 "%ldc") at snprintf_chk.c:29
  #8  0x000055b8c7aea59f in snprintf (__fmt=0x55b8c7b872a6 "%ldc", __n=1025, __s=0x55b8c7c096a5 <IObuff+5> "t' item") at /usr/include/bits/stdio2.h:54
  #9  uc_list (name=name@entry=0x55b8c8351788 "Explore", name_len=name_len@entry=7) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/usercmd.c:534
  #10 0x000055b8c7aeb8a0 in ex_command (eap=0x7fffdc350e60) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/usercmd.c:1009
  #11 0x000055b8c7972537 in execute_cmd0 (retv=retv@entry=0x7fffdc350e54, eap=eap@entry=0x7fffdc350e60, errormsg=errormsg@entry=0x7fffdc350e58, preview=preview@entry=false) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/ex_docmd.c:1620
  #12 0x000055b8c7975c55 in do_one_cmd (cmdlinep=cmdlinep@entry=0x7fffdc3510b8, flags=flags@entry=0, cstack=cstack@entry=0x7fffdc351140, fgetline=fgetline@entry=0x55b8c79882b8 <getexline>, cookie=cookie@entry=0x0) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/ex_docmd.c:2279
  #13 0x000055b8c79767fe in do_cmdline (cmdline=<optimized out>, fgetline=0x55b8c79882b8 <getexline>, cookie=0x0, flags=0) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/ex_docmd.c:578
  #14 0x000055b8c7a17463 in nv_colon (cap=0x7fffdc351780) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/normal.c:3228
  #15 0x000055b8c7a11b35 in normal_execute (state=0x7fffdc351700, key=<optimized out>) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/normal.c:1196
  #16 0x000055b8c7ab0994 in state_enter (s=0x7fffdc351700) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/state.c:99
  #17 0x000055b8c7a0ef68 in normal_enter (cmdwin=false, noexmode=false) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/normal.c:497
  #18 0x000055b8c78a0640 in main (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/main.c:641
Diffstat:
Msrc/nvim/usercmd.c | 34+++++++++++++++++++---------------
Mtest/functional/ex_cmds/excmd_spec.lua | 5+++++
2 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c @@ -469,7 +469,7 @@ static void uc_list(char *name, size_t name_len) } // Special cases - int len = 4; + size_t len = 4; if (a & EX_BANG) { msg_putchar('!'); len--; @@ -491,7 +491,7 @@ static void uc_list(char *name, size_t name_len) } msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D)); - len = (int)strlen(cmd->uc_name) + 4; + len = strlen(cmd->uc_name) + 4; do { msg_putchar(' '); @@ -500,7 +500,7 @@ static void uc_list(char *name, size_t name_len) // "over" is how much longer the name is than the column width for // the name, we'll try to align what comes after. - const int over = len - 22; + const int64_t over = (int64_t)len - 22; len = 0; // Arguments @@ -524,20 +524,22 @@ static void uc_list(char *name, size_t name_len) do { IObuff[len++] = ' '; - } while (len < 5 - over); + } while ((int64_t)len < 5 - over); // Address / Range if (a & (EX_RANGE | EX_COUNT)) { if (a & EX_COUNT) { // -count=N - snprintf(IObuff + len, IOSIZE, "%" PRId64 "c", cmd->uc_def); - len += (int)strlen(IObuff + len); + int rc = snprintf(IObuff + len, IOSIZE - len, "%" PRId64 "c", cmd->uc_def); + assert(rc > 0); + len += (size_t)rc; } else if (a & EX_DFLALL) { IObuff[len++] = '%'; } else if (cmd->uc_def >= 0) { // -range=N - snprintf(IObuff + len, IOSIZE, "%" PRId64 "", cmd->uc_def); - len += (int)strlen(IObuff + len); + int rc = snprintf(IObuff + len, IOSIZE - len, "%" PRId64 "", cmd->uc_def); + assert(rc > 0); + len += (size_t)rc; } else { IObuff[len++] = '.'; } @@ -545,32 +547,34 @@ static void uc_list(char *name, size_t name_len) do { IObuff[len++] = ' '; - } while (len < 8 - over); + } while ((int64_t)len < 8 - over); // Address Type for (j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { - STRCPY(IObuff + len, addr_type_complete[j].shortname); - len += (int)strlen(IObuff + len); + int rc = snprintf(IObuff + len, IOSIZE - len, "%s", addr_type_complete[j].shortname); + assert(rc > 0); + len += (size_t)rc; break; } } do { IObuff[len++] = ' '; - } while (len < 13 - over); + } while ((int64_t)len < 13 - over); // Completion char *cmd_compl = get_command_complete(cmd->uc_compl); if (cmd_compl != NULL) { - STRCPY(IObuff + len, get_command_complete(cmd->uc_compl)); - len += (int)strlen(IObuff + len); + int rc = snprintf(IObuff + len, IOSIZE - len, "%s", get_command_complete(cmd->uc_compl)); + assert(rc > 0); + len += (size_t)rc; } do { IObuff[len++] = ' '; - } while (len < 25 - over); + } while ((int64_t)len < 25 - over); IObuff[len] = '\0'; msg_outtrans(IObuff); diff --git a/test/functional/ex_cmds/excmd_spec.lua b/test/functional/ex_cmds/excmd_spec.lua @@ -28,6 +28,11 @@ describe('Ex cmds', function() assert_alive() end) + it('listing long user command does not crash', function() + command('execute "command" repeat("T", 255) ":"') + command('command') + end) + it(':def is an unknown command #23149', function() eq('Vim:E492: Not an editor command: def', pcall_err(command, 'def')) eq(1, funcs.exists(':d'))