vi: fix buffer overrun; code shrink

It was possible for get_input_line() to store its NUL terminator
one character beyond the end of its buffer.

Code shrink in colon():

- Certain colon commands can be matched exactly, as any shorter
  string would be matched earlier, e.g. ':wq' versus ':write'.

- Command matching is now case sensitive so there's no need to
  check for 'N' or 'Q' suffixes.

- Rewrite how commands and arguments are split.

function                                             old     new   delta
colon                                               3848    3751     -97
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-97)             Total: -97 bytes

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2021-04-25 11:55:01 +01:00 committed by Denys Vlasenko
parent dadd909746
commit 852ffbee34

View File

@ -1191,7 +1191,7 @@ static char *get_input_line(const char *prompt)
write1(prompt); // write out the :, /, or ? prompt write1(prompt); // write out the :, /, or ? prompt
i = strlen(buf); i = strlen(buf);
while (i < MAX_INPUT_LEN) { while (i < MAX_INPUT_LEN - 1) {
c = get_one_char(); c = get_one_char();
if (c == '\n' || c == '\r' || c == 27) if (c == '\n' || c == '\r' || c == 27)
break; // this is end of input break; // this is end of input
@ -2572,7 +2572,7 @@ static void colon(char *buf)
if (cnt == 0) if (cnt == 0)
return; return;
if (strncmp(p, "quit", cnt) == 0 if (strncmp(p, "quit", cnt) == 0
|| strncmp(p, "q!", cnt) == 0 || strcmp(p, "q!") == 0
) { ) {
if (modified_count && p[1] != '!') { if (modified_count && p[1] != '!') {
status_line_bold("No write since last change (:%s! overrides)", p); status_line_bold("No write since last change (:%s! overrides)", p);
@ -2582,8 +2582,8 @@ static void colon(char *buf)
return; return;
} }
if (strncmp(p, "write", cnt) == 0 if (strncmp(p, "write", cnt) == 0
|| strncmp(p, "wq", cnt) == 0 || strcmp(p, "wq") == 0
|| strncmp(p, "wn", cnt) == 0 || strcmp(p, "wn") == 0
|| (p[0] == 'x' && !p[1]) || (p[0] == 'x' && !p[1])
) { ) {
if (modified_count != 0 || p[0] != 'x') { if (modified_count != 0 || p[0] != 'x') {
@ -2601,7 +2601,6 @@ static void colon(char *buf)
); );
if (p[0] == 'x' if (p[0] == 'x'
|| p[1] == 'q' || p[1] == 'n' || p[1] == 'q' || p[1] == 'n'
|| p[1] == 'Q' || p[1] == 'N'
) { ) {
editing = 0; editing = 0;
} }
@ -2621,10 +2620,9 @@ static void colon(char *buf)
#else #else
char c, *buf1, *q, *r; char c, *buf1, *q, *r;
char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args;
int i, l, li, b, e; int i, l, li, b, e;
int useforce; int useforce;
char *orig_buf;
// :3154 // if (-e line 3154) goto it else stay put // :3154 // if (-e line 3154) goto it else stay put
// :4,33w! foo // write a portion of buffer to file "foo" // :4,33w! foo // write a portion of buffer to file "foo"
@ -2652,35 +2650,29 @@ static void colon(char *buf)
fn = current_filename; fn = current_filename;
// look for optional address(es) :. :1 :1,9 :'q,'a :% // look for optional address(es) :. :1 :1,9 :'q,'a :%
orig_buf = buf; buf1 = buf;
buf = get_address(buf, &b, &e); buf = get_address(buf, &b, &e);
if (buf == NULL) { if (buf == NULL) {
status_line_bold("Bad address: %s", orig_buf); status_line_bold("Bad address: %s", buf1);
goto ret; goto ret;
} }
# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
// remember orig command line
orig_buf = buf;
# endif
// get the COMMAND into cmd[] // get the COMMAND into cmd[]
strcpy(cmd, buf);
buf1 = cmd; buf1 = cmd;
while (*buf != '\0') { while (!isspace(*buf1) && *buf1 != '\0') {
if (isspace(*buf)) buf1++;
break;
*buf1++ = *buf++;
} }
*buf1 = '\0'; cmdend = buf1;
// get any ARGuments // get any ARGuments
while (isblank(*buf)) while (isblank(*buf1))
buf++; buf1++;
strcpy(args, buf); args = buf1;
*cmdend = '\0';
useforce = FALSE; useforce = FALSE;
buf1 = last_char_is(cmd, '!'); if (cmdend > cmd && cmdend[-1] == '!') {
if (buf1) {
useforce = TRUE; useforce = TRUE;
*buf1 = '\0'; // get rid of ! cmdend[-1] = '\0'; // get rid of !
} }
// assume the command will want a range, certain commands // assume the command will want a range, certain commands
// (read, substitute) need to adjust these assumptions // (read, substitute) need to adjust these assumptions
@ -2718,7 +2710,7 @@ static void colon(char *buf)
// :!ls run the <cmd> // :!ls run the <cmd>
go_bottom_and_clear_to_eol(); go_bottom_and_clear_to_eol();
cookmode(); cookmode();
retcode = system(orig_buf + 1); // run the cmd retcode = system(buf + 1); // run the cmd
if (retcode) if (retcode)
printf("\nshell returned %i\n\n", retcode); printf("\nshell returned %i\n\n", retcode);
rawmode(); rawmode();
@ -2969,8 +2961,8 @@ static void colon(char *buf)
// F points to the "find" pattern // F points to the "find" pattern
// R points to the "replace" pattern // R points to the "replace" pattern
// replace the cmd line delimiters "/" with NULs // replace the cmd line delimiters "/" with NULs
c = orig_buf[1]; // what is the delimiter c = buf[1]; // what is the delimiter
F = orig_buf + 2; // start of "find" F = buf + 2; // start of "find"
R = strchr(F, c); // middle delimiter R = strchr(F, c); // middle delimiter
if (!R) if (!R)
goto colon_s_fail; goto colon_s_fail;
@ -3039,8 +3031,8 @@ static void colon(char *buf)
} else if (strncmp(cmd, "version", i) == 0) { // show software version } else if (strncmp(cmd, "version", i) == 0) { // show software version
status_line(BB_VER); status_line(BB_VER);
} else if (strncmp(cmd, "write", i) == 0 // write text to file } else if (strncmp(cmd, "write", i) == 0 // write text to file
|| strncmp(cmd, "wq", i) == 0 || strcmp(cmd, "wq") == 0
|| strncmp(cmd, "wn", i) == 0 || strcmp(cmd, "wn") == 0
|| (cmd[0] == 'x' && !cmd[1]) || (cmd[0] == 'x' && !cmd[1])
) { ) {
int size; int size;
@ -3096,7 +3088,6 @@ static void colon(char *buf)
} }
if (cmd[0] == 'x' if (cmd[0] == 'x'
|| cmd[1] == 'q' || cmd[1] == 'n' || cmd[1] == 'q' || cmd[1] == 'n'
|| cmd[1] == 'Q' || cmd[1] == 'N'
) { ) {
editing = 0; editing = 0;
} }