Skip to content

Commit 9e79f74

Browse files
authored
fix(usercmd): Fix buffer overflow in uc_list() (neovim#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 neovim#2 0x00007f3eb8b42aee in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26 neovim#3 0x00007f3eb8b2b87f in __GI_abort () at abort.c:79 neovim#4 0x00007f3eb8b2c60f in __libc_message (fmt=fmt@entry=0x7f3eb8ca72e6 "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:150 neovim#5 0x00007f3eb8c27b29 in __GI___fortify_fail (msg=msg@entry=0x7f3eb8ca728c "buffer overflow detected") at fortify_fail.c:24 neovim#6 0x00007f3eb8c26364 in __GI___chk_fail () at chk_fail.c:28 neovim#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 neovim#8 0x000055b8c7aea59f in snprintf (__fmt=0x55b8c7b872a6 "%ldc", __n=1025, __s=0x55b8c7c096a5 <IObuff+5> "t' item") at /usr/include/bits/stdio2.h:54 neovim#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 neovim#10 0x000055b8c7aeb8a0 in ex_command (eap=0x7fffdc350e60) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/usercmd.c:1009 neovim#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 neovim#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 neovim#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 neovim#14 0x000055b8c7a17463 in nv_colon (cap=0x7fffdc351780) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/normal.c:3228 neovim#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 neovim#16 0x000055b8c7ab0994 in state_enter (s=0x7fffdc351700) at /usr/src/debug/neovim-0.9.0-1.fc38.x86_64/src/nvim/state.c:99 neovim#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 neovim#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
1 parent ebfb639 commit 9e79f74

File tree

2 files changed

+24
-15
lines changed

2 files changed

+24
-15
lines changed

src/nvim/usercmd.c

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ static void uc_list(char *name, size_t name_len)
469469
}
470470

471471
// Special cases
472-
int len = 4;
472+
size_t len = 4;
473473
if (a & EX_BANG) {
474474
msg_putchar('!');
475475
len--;
@@ -491,7 +491,7 @@ static void uc_list(char *name, size_t name_len)
491491
}
492492

493493
msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D));
494-
len = (int)strlen(cmd->uc_name) + 4;
494+
len = strlen(cmd->uc_name) + 4;
495495

496496
do {
497497
msg_putchar(' ');
@@ -500,7 +500,7 @@ static void uc_list(char *name, size_t name_len)
500500

501501
// "over" is how much longer the name is than the column width for
502502
// the name, we'll try to align what comes after.
503-
const int over = len - 22;
503+
const int64_t over = (int64_t)len - 22;
504504
len = 0;
505505

506506
// Arguments
@@ -524,53 +524,57 @@ static void uc_list(char *name, size_t name_len)
524524

525525
do {
526526
IObuff[len++] = ' ';
527-
} while (len < 5 - over);
527+
} while ((int64_t)len < 5 - over);
528528

529529
// Address / Range
530530
if (a & (EX_RANGE | EX_COUNT)) {
531531
if (a & EX_COUNT) {
532532
// -count=N
533-
snprintf(IObuff + len, IOSIZE, "%" PRId64 "c", cmd->uc_def);
534-
len += (int)strlen(IObuff + len);
533+
int rc = snprintf(IObuff + len, IOSIZE - len, "%" PRId64 "c", cmd->uc_def);
534+
assert(rc > 0);
535+
len += (size_t)rc;
535536
} else if (a & EX_DFLALL) {
536537
IObuff[len++] = '%';
537538
} else if (cmd->uc_def >= 0) {
538539
// -range=N
539-
snprintf(IObuff + len, IOSIZE, "%" PRId64 "", cmd->uc_def);
540-
len += (int)strlen(IObuff + len);
540+
int rc = snprintf(IObuff + len, IOSIZE - len, "%" PRId64 "", cmd->uc_def);
541+
assert(rc > 0);
542+
len += (size_t)rc;
541543
} else {
542544
IObuff[len++] = '.';
543545
}
544546
}
545547

546548
do {
547549
IObuff[len++] = ' ';
548-
} while (len < 8 - over);
550+
} while ((int64_t)len < 8 - over);
549551

550552
// Address Type
551553
for (j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) {
552554
if (addr_type_complete[j].expand != ADDR_LINES
553555
&& addr_type_complete[j].expand == cmd->uc_addr_type) {
554-
STRCPY(IObuff + len, addr_type_complete[j].shortname);
555-
len += (int)strlen(IObuff + len);
556+
int rc = snprintf(IObuff + len, IOSIZE - len, "%s", addr_type_complete[j].shortname);
557+
assert(rc > 0);
558+
len += (size_t)rc;
556559
break;
557560
}
558561
}
559562

560563
do {
561564
IObuff[len++] = ' ';
562-
} while (len < 13 - over);
565+
} while ((int64_t)len < 13 - over);
563566

564567
// Completion
565568
char *cmd_compl = get_command_complete(cmd->uc_compl);
566569
if (cmd_compl != NULL) {
567-
STRCPY(IObuff + len, get_command_complete(cmd->uc_compl));
568-
len += (int)strlen(IObuff + len);
570+
int rc = snprintf(IObuff + len, IOSIZE - len, "%s", get_command_complete(cmd->uc_compl));
571+
assert(rc > 0);
572+
len += (size_t)rc;
569573
}
570574

571575
do {
572576
IObuff[len++] = ' ';
573-
} while (len < 25 - over);
577+
} while ((int64_t)len < 25 - over);
574578

575579
IObuff[len] = '\0';
576580
msg_outtrans(IObuff);

test/functional/ex_cmds/excmd_spec.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ describe('Ex cmds', function()
2828
assert_alive()
2929
end)
3030

31+
it('listing long user command does not crash', function()
32+
command('execute "command" repeat("T", 255) ":"')
33+
command('command')
34+
end)
35+
3136
it(':def is an unknown command #23149', function()
3237
eq('Vim:E492: Not an editor command: def', pcall_err(command, 'def'))
3338
eq(1, funcs.exists(':d'))

0 commit comments

Comments
 (0)