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;