Interrupted fputc followed by fprintf in _IOLBF mode causes core dump

From: Guy Yur <guyyur_at_gmail.com>
Date: Fri, 25 Mar 2022 12:18:40 UTC
Hi,

dhcpcd on head (Mar 24) and 13.1-BETA2 crashes in fprintf/__sfvwrite.
It doesn't crash if If I revert the __sflush/__sfvwrite commits:
86a16ada1ea608408cec370171d9f59353e97c77 and
bafaa70b6f9098d83d074968c8e6747ecec1e118.

Stack trace:
0  memcpy () at /usr/src/lib/libc/amd64/string/memmove.S:314
#1  0x00000008221c300a in __sfvwrite (fp=<optimized out>,
uio=0x8207ad338) at /usr/src/lib/libc/stdio/fvwrite.c:182
#2  0x00000008221cc631 in __sprint (fp=0x26fffe, uio=0x8207ad2d7,
locale=<optimized out>) at /usr/src/lib/libc/stdio/vfprintf.c:166
#3  io_flush (iop=0x8207ad330, locale=<optimized out>) at
/usr/src/lib/libc/stdio/printfcommon.h:157
#4  __vfprintf (fp=fp@entry=0x8222892d0, locale=locale@entry=0x822288ab8
<__xlocale_global_locale>, fmt0=<optimized out>, fmt0@entry=0x204182 "%s",
     ap=<optimized out>, ap@entry=0x8207ad4e0) at
/usr/src/lib/libc/stdio/vfprintf.c:1033
#5  0x00000008221c8aea in vfprintf_l (fp=0x8222892d0, locale=0x822288ab8
<__xlocale_global_locale>, fmt0=0x204182 "%s", ap=0x8207ad4e0)
     at /usr/src/lib/libc/stdio/vfprintf.c:285
#6  0x0000000000222efa in vlogprintf_r (ctx=0x270820 <_logctx>,
stream=0x8222892d0, fmt=0x204182 "%s", args=0x8207adad0) at logerr.c:186
...

(gdb) frame 5
#5  0x00000008221c8aea in vfprintf_l (fp=0x8222892d0, locale=0x822288ab8
<__xlocale_global_locale>, fmt0=0x204182 "%s", ap=0x8207ad4e0)
     at /usr/src/lib/libc/stdio/vfprintf.c:285
285                     ret = __vfprintf(fp, locale, fmt0, ap);
(gdb) p *fp
$1 = {_p = 0x27084f <_logctx+47> "e21:3e7c\n42a/64\n", _r = 0, _w =
-1025, _flags = 2057, _file = 2, _bf = {_base = 0x270820 <_logctx>
"*\"", _size = 1024},
  _lbfsize = -1024, _cookie = 0x8222892d0, _close = 0x8221c7b40
<__sclose>, _read = 0x8221c7af0 <__sread>, _seek = 0x8221c7b30
<__sseek>,
  _write = 0x8221c7b10 <__swrite>, _ub = {_base = 0x0, _size = 0}, _up
= 0x0, _ur = 0, _ubuf = "\000\000", _nbuf = "", _lb = {_base = 0x0,
_size = 0},
  _blksize = 4096, _offset = 0, _fl_mutex = 0x0, _fl_owner = 0x0,
_fl_count = 0, _orientation = -1, _mbstate = {__mbstate8 = '\000'
<repeats 127 times>,
    _mbstateL = 0}, _flags2 = 0}

(gdb) frame 1
#1  0x00000008221c300a in __sfvwrite (fp=<optimized out>,
uio=0x8207ad338) at /usr/src/lib/libc/stdio/fvwrite.c:182
182                                     COPY(w);
(gdb) p w
$4 = -1


The dhcpcd flow leading to the crash:
1. init with setvbuf _IOLBF on stderr
https://github.com/NetworkConfiguration/dhcpcd/blob/master/src/logerr.c#L453

2. fputc with newline called on stderr but is interrupted
https://github.com/NetworkConfiguration/dhcpcd/blob/master/src/logerr.c#L187

3. next event received, vfprintf is called on stderr and crashes
https://github.com/NetworkConfiguration/dhcpcd/blob/master/src/logerr.c#L186


Simple program that eventually crashes:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

static void
alrm(int signo __unused)
{
        alarm(1);
}

char buf[1024]; /* use global to not corrupt stack trace in core dump */

int main()
{
        struct sigaction sa;

        sa.sa_handler = alrm;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGALRM, &sa, NULL);

        setvbuf(stderr, buf, _IOLBF, sizeof(buf));
        alarm(1);
        while (!ferror(stderr)) {
                fputc('\n', stderr);
        }
        fprintf(stderr, "%s", "a");
        return 0;
}


Regards,
Guy Yur