ex_cmd() in usr.bin/vi/ex/ex.c computes the remaining command
length with unsigned arithmetic that underflows when the +cmd
inner loop consumes every byte and "discard" (escape pairs)
reaches save_cmd - cp:

        ecp->clen = ((ecp->save_cmd - ecp->cp) - 1) - discard;

The resulting (size_t)-1 flows into argv_exp2() -> argv_fexp()
at ex_argv.c:305 which loops until cmd walks off mapped memory.

Minimum reproducer (7 bytes, \x16 = literal-next):

        $ printf 'x\n' > t
        $ printf 'e+"p\x16o\n' | MALLOC_OPTIONS=S vi -e t
        Segmentation fault (core dumped)

Reproduced on OpenBSD 7.9 arm64, amd64 and i386.  Found by
AFL++ fuzzing of ex(1).

OK?

Index: usr.bin/vi/ex/ex.c
===================================================================
RCS file: /cvs/src/usr.bin/vi/ex/ex.c,v
retrieving revision 1.23
diff -u -p -r1.23 ex.c
--- usr.bin/vi/ex/ex.c  23 Jun 2023 15:06:45 -0000      1.23
+++ usr.bin/vi/ex/ex.c
@@ -801,7 +801,8 @@ ex_cmd(SCR *sp)
        ecp->cp = ecp->save_cmd;
        ecp->save_cmd = p;
        ecp->save_cmdlen = ecp->clen;
-       ecp->clen = ((ecp->save_cmd - ecp->cp) - 1) - discard;
+       len = ecp->save_cmd - ecp->cp;
+       ecp->clen = (len > 1 + discard) ? len - 1 - discard : 0;

        /*
         * QUOTING NOTE:

Reply via email to