As reported by kn@ on bugs [1], here's a fix for the issue. In short, if
you start vi(1)/ex(1) with a named buffer that's not associated with an
existing file and then run the ex_edit command (:e) with zero args, it
frees strings for the name of the buffer and the temp file. At quit, the
cleanup code tries to re-use those values still referenced by the
screen.

[1] https://marc.info/?l=openbsd-bugs&w=2

I don't know the vi codebase, so I can't tell if this is the right way
to fix this. It's a minimal change, but I use vi mostly for quick edits
and commit messages so would appreciate anyone with more knowledge here
to chime in.

Feedback or OK?

-dv

Reproduction (via ex since it's easier to show in email):

minmin[vi]$ ex foo
foo: new file: line 1
:e
:e bar
File is a temporary; exit will discard modifications
:q!
ex(23987) in free(): write after free 0x26bc2314ec0
Abort trap (core dumped)
minmin[vi]$ build/obj/vi -e foo
foo: new file: line 1
:e
:e bar
bar: new file: line 1
:q!
minmin[vi]$ echo $?
0

Some additional context to that "goto err" in the diff:

   381  err:
   382          free(frp->name);
   383          frp->name = NULL;
   384          if (frp->tname != NULL) {
   385                  (void)unlink(frp->tname);
   386                  free(frp->tname);
   387                  frp->tname = NULL;
   388          }

The diff:

diff 8095b13035d3c80c255344b9166e7f4ff88e61e3 /usr/src
blob - 0b6ae026533e5696a31f4bd87291ccd1d7d5e58f
file + usr.bin/vi/common/exf.c
--- usr.bin/vi/common/exf.c
+++ usr.bin/vi/common/exf.c
@@ -170,12 +170,20 @@ file_init(SCR *sp, FREF *frp, char *rcv_name, int flag
         * If no name or backing file, for whatever reason, create a backing
         * temporary file, saving the temp file name so we can later unlink
         * it.  If the user never named this file, copy the temporary file name
         * to the real name (we display that until the user renames it).
         */
        oname = frp->name;
+
+       /*
+        * User is editing a named file that doesn't exist yet other than as a
+        * temporary file.
+        */
+       if (!exists && oname != NULL && frp->tname != NULL)
+               return (1);
+
        if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) {
                /*
                 * Don't try to create a temporary support file twice.
                 */
                if (frp->tname != NULL)
                        goto err;

Reply via email to