Download raw body.
vi(1) expandtab segfault bug fixed
Hello,
while using vi(1) with expandtab I noticed a situation that caused it to
segfault that I managed to fix.
The bug happens also in nvi from ports, and in all the other nvi that I
testes.
The procedure that causes the bug:
* open vi with set expandtab
* yy -> yank something
* :di b -> shows that the default buffer have the text copied
* :!ls >/dev/null run an external command without filtering lines, all
commands are the same
* :di b -> already shows something strange, outputting:
********** default buffer (character mode)
note that when the buffers are empty :di b shows:
No cut buffers to display.
* p -> trying to paste segfault
A bug that does not segfault occurs also with the bang command, but when
filtering lines:
* open vi with set expandtab
* yy -> yank something and then move to an other line
* :di b -> shows that the default buffer have the text copied
* :.!ls >/dev/null run an external command filtering any range of lines,
both one address or two is the same, noticing the content of the line
you are on
* :di b -> shows that the line(s) being put into the default buffer
is(are) the line(s) that is(are) now under the cursor, not the original
deleted ones
Running the bang without filtering files leaves the sp->gp->dcb_store as
a dangling pointer.
The stacktrace of when the bug happens is:
* ex_bang in ex/ex_bang.c line 176
* ex_retab in ex/ex_shift.c line 59
* shift in ex/ex_shift.c line 82
* cut in common/cut.c line 137
* cut_line in common/cut.c line 242
* db_get in common/line.c line 88
When cut_line returns an error, by going to the goto cut_line_err, it
skps the line 160 that repoints the default buffer on each pass:
sp->gp->dcbp = cbp; /* Repoint the default buffer on each pass. */
thus leaving dcbp in an invalid state.
The changes that I made:
* added sp->gp->dcbp = NULL in the goto cut_line_err.
With just this change, the bug is still present but at least there is
not the segfault since the default buffer will be pointing to NULL, so
it will be as if anything has ever being copied, :di b will show:
No cut buffers to display.
* then I added a check at the start of cut that return error if any of
the line numbers of the marks are out-of-band, since otherwise it will
fail below in cut_line
* going up the stackframe, in shift I made so that the cut command is
run just when real shifts are happening, not when doing expandtab
* then in ex_bang I made so expandtab is applied just when at least an
address was specified since otherwise there is nothing to expand
* then at last I noticed that in common/line.c OOBLNO was used in some
places but not in others, so I made it consistent by using it in all
occurances
Bye,
jerry
diff --git common/cut.c common/cut.c
index 7ed134693a5..e77a8ed22a9 100644
--- common/cut.c
+++ common/cut.c
@@ -69,6 +69,10 @@ cut(SCR *sp, CHAR_T *namep, MARK *fm, MARK *tm, int flags)
recno_t lno;
int append, copy_one, copy_def;
+ /* Check if the line numbers are out-of-band */
+ if (fm->lno == OOBLNO || tm->lno == OOBLNO)
+ return (1);
+
/*
* If the user specified a buffer, put it there. (This may require
* a copy into the numeric buffers. We do the copy so that we don't
@@ -175,6 +179,7 @@ cut_line_err:
text_lfree(&cbp->textq);
cbp->len = 0;
cbp->flags = 0;
+ sp->gp->dcbp = NULL;
return (1);
}
diff --git common/line.c common/line.c
index 7dfbdbf2756..84f04b27c0f 100644
--- common/line.c
+++ common/line.c
@@ -49,7 +49,7 @@ db_eget(SCR *sp, recno_t lno, char **pp, size_t *lenp, int *isemptyp)
* line in an empty file, find the last line of the file; db_last
* fails loudly.
*/
- if ((lno == 0 || lno == 1) && db_last(sp, &l1))
+ if ((lno == OOBLNO || lno == 1) && db_last(sp, &l1))
return (1);
/* If the file isn't empty, fail loudly. */
@@ -84,7 +84,7 @@ db_get(SCR *sp, recno_t lno, u_int32_t flags, char **pp, size_t *lenp)
* have to have an OOB condition for the look-aside into the input
* buffer anyway.
*/
- if (lno == 0)
+ if (lno == OOBLNO)
goto err1;
/* Check for no underlying file. */
diff --git ex/ex_bang.c ex/ex_bang.c
index cdb7bf2ec57..4ef93c1c678 100644
--- ex/ex_bang.c
+++ ex/ex_bang.c
@@ -171,8 +171,8 @@ ex_bang(SCR *sp, EXCMD *cmdp)
if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
(void)ex_puts(sp, "!\n");
- /* Apply expandtab to the new text */
- if (O_ISSET(sp, O_EXPANDTAB))
+ /* If addresses were specified, apply expandtab to the new text */
+ if (cmdp->addrcnt != 0 && O_ISSET(sp, O_EXPANDTAB))
ex_retab(sp, cmdp);
/*
diff --git ex/ex_shift.c ex/ex_shift.c
index e7f45c1916c..8f6edad0f2d 100644
--- ex/ex_shift.c
+++ ex/ex_shift.c
@@ -78,9 +78,13 @@ shift(SCR *sp, EXCMD *cmdp, enum which rl)
return (0);
}
- /* Copy the lines being shifted into the unnamed buffer. */
- if (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
- return (1);
+ /*
+ * When not doing re-expand tabs, copy the lines being shifted into
+ * the unnamed buffer.
+ */
+ if (rl != RETAB &&
+ cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
+ return (1);
/*
* The historic version of vi permitted the user to string any number
vi(1) expandtab segfault bug fixed