Apologies, the "pfnote() stack overwrite" is not actually a stack
overwrite. I apparently forgot how `strrchr()` works for a minute, and
thus I misinterpreted the crash result. Upon further inspection the
actual issue at hand is an out-of-bounds stack read of one byte on the
`nbuf` variable. Here is the updated writeup for this specific bug.

# pfnote() stack out-of-bounds read

In `pfnote()` it is possible that `curfile` is set from an input tags
file which was loaded in `preload_entries()`. In this case the filename
can be controlled by an input file. If the entry matches the `main`
tag, is 254 bytes followed by a ".", then the access will go of out
bounds on the `strrchr()`. This is due to the `snprintf()` writing to a
(1+255+1) [257] byte buffer with the 255 bytes from the filename and
trailing ".", and the null terminator. At this point the buffer is
completely filled. In this case the `strrchr()` will find the '.' on
the last character in the buffer, prior to a '\0', however it will
access at byte `fp[2]`, which is one byte past the null terminator.
Thus an out-of-bounds read of one byte past the `nbuf` buffer.

This bug can also be hit by parsing a file with the same filename, 254
non-'.' characters followed by a '.' (empty extension).

```c
(void)snprintf(nbuf, sizeof nbuf, "M%s", fp);
fp = strrchr(nbuf, '.');
if (fp && !fp[2])
    *fp = EOS;
```

```
pleb@gamey:~/openbsd_src/usr.bin/ctags$ ./a.out -u asdf
=================================================================
==25068==ERROR: AddressSanitizer: stack-buffer-overflow on address
    0x7ffe92b03d61 at pc 0x5606a636e0c0
    bp 0x7ffe92b03c10 sp 0x7ffe92b03c08
    READ of size 1 at 0x7ffe92b03d61 thread T0
    #0 0x5606a636e0bf in pfnote
    /home/pleb/openbsd_src/usr.bin/ctags/tree.c:72
    #1 0x5606a636b9b9 in preload_entries
    /home/pleb/openbsd_src/usr.bin/ctags/ctags.c:309
    #2 0x5606a636aaae in main
    /home/pleb/openbsd_src/usr.bin/ctags/ctags.c:129
    #3 0x7f0b078ac09a in __libc_start_main ../csu/libc-start.c:308
    #4 0x5606a6368329 in _start
    (/home/pleb/openbsd_src/usr.bin/ctags/a.out+0x3329)
```

## PoC

Create a tags file with the contents
"main\t<254 non-'.' characters>.\t/^/" and save it to "tags". Then
invoke `ctags` in `-u` mode: `ctags -u tags`

Alternatively, create a file with the contents "main(){" and save it
to a file named "<254 'A' characters>.", and invoke `ctags <filename>`

Reply via email to