Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 31 Jan 2016 01:50:55 +0000
From:      bugzilla-noreply@freebsd.org
To:        freebsd-bugs@FreeBSD.org
Subject:   [Bug 206770] 11.0-CURRENT/clang380-import: libc/stdio uninitialized pointer use (exposed via powerpc 32-bit context)
Message-ID:  <bug-206770-8@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D206770

            Bug ID: 206770
           Summary: 11.0-CURRENT/clang380-import: libc/stdio uninitialized
                    pointer use (exposed via powerpc 32-bit context)
           Product: Base System
           Version: 11.0-CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: bin
          Assignee: freebsd-bugs@FreeBSD.org
          Reporter: markmi@dsl-only.net

Using projects/clang380-import (-r294962 currently) I've been experimenting
with a clang based powerpc (32-bit) buildworld (mixed with a gcc421 based
buildkernel). Things boot and much operates but I ran into signals getting
segmentation violations during signal handlers.

It turn out the ones that I've looked at so far are using routines similar =
to
snprinf in the signal handlers and I've found that the call chains involved
have an uninitialized pointer in use.

The following uses snprintf as a starting context but the more common centr=
al
point is __vfprintf and what it does/calls for such "string output" routine=
s.

Unfortunately the reason is spread out in the code so it takes a bit to
describe the context for the uninitialized pointer that I expect is involve=
d.

To start the description I note the actual, low-level failure point:

#0  0x419a89c8 in memcpy (dst0=3D0xffffd734, src0=3D<optimized out>,
length=3D<optimized out>) at /usr/src/lib/libc/string/bcopy.c:124
124                             TLOOP1(*--dst =3D *--src);

In the assembler code for this is the the *--src access that gets the
segmentation violation. I do not justify that claim here but use that fact
later.

So what leads up to that? Going the other way, starting from the use of
snprintf. . .

snprintf(char * __restrict str, size_t n, char const * __restrict fmt, ...)
sets up its __vfprintf(FILE *fp, locale_t locale, const char *fmt0, va_list=
 ap)
use via:

       va_list ap;
       FILE f =3D FAKE_FILE;
. . .
       va_start(ap, fmt);
       f._flags =3D __SWR | __SSTR;
       f._bf._base =3D f._p =3D (unsigned char *)str;
       f._bf._size =3D f._w =3D n;
       ret =3D __vfprintf(&f, __get_locale(), fmt, ap);

so at the __vfprintf call f._p reference the buffer that __vfprintf's str
references. __vfprintf in turn does (in part):

       struct io_state io;     /* I/O buffering state */
. . .
       io_init(&io, fp);

where io is on-stack (not implicitly initialized). The io_init does:

#define NIOV 8
struct io_state {
       FILE *fp;
       struct __suio uio;      /* output information: summary */
       struct __siov iov[NIOV];/* ... and individual io vectors */
};

static inline void
io_init(struct io_state *iop, FILE *fp)
{

       iop->uio.uio_iov =3D iop->iov;
       iop->uio.uio_resid =3D 0;
       iop->uio.uio_iovcnt =3D 0;
       iop->fp =3D fp;
}

where (on stack as part of __vfprintf's io):

struct __siov {
       void    *iov_base;
       size_t  iov_len;
};
struct __suio {
       struct  __siov *uio_iov;
       int     uio_iovcnt;
       int     uio_resid;
};

So via __vfprintf's io.fp->_p the str buffer is accessible for outputting t=
o.

But in none of this or other code that I've looked at for this snprintf use
case have I found code that initializes the involved io.uio.uio_iov->iov_ba=
se
(i.e., io.iov[0].iov_base) to point to anything specific. (Nor is iov_base's
matching iov_len initialized.)

Here is a stab at finding all the initializations of iov_base fields:

# grep "iov_base.*=3D" /usr/src/lib/libc/stdio/*
/usr/src/lib/libc/stdio/fputs.c:        iov.iov_base =3D (void *)s;
/usr/src/lib/libc/stdio/fputws.c:       iov.iov_base =3D buf;
/usr/src/lib/libc/stdio/fwrite.c:       iov.iov_base =3D (void *)buf;
/usr/src/lib/libc/stdio/perror.c:               v->iov_base =3D (char *)s;
/usr/src/lib/libc/stdio/perror.c:               v->iov_base =3D ": ";
/usr/src/lib/libc/stdio/perror.c:       v->iov_base =3D msgbuf;
/usr/src/lib/libc/stdio/perror.c:       v->iov_base =3D "\n";
/usr/src/lib/libc/stdio/printfcommon.h: iop->iov[iop->uio.uio_iovcnt].iov_b=
ase
=3D (char *)ptr;
/usr/src/lib/libc/stdio/puts.c: iov[0].iov_base =3D (void *)s;
/usr/src/lib/libc/stdio/puts.c: iov[1].iov_base =3D "\n";
/usr/src/lib/libc/stdio/putw.c: iov.iov_base =3D &w;
/usr/src/lib/libc/stdio/vfwprintf.c:    iov.iov_base =3D buf;
/usr/src/lib/libc/stdio/xprintf.c:      io->iovp->iov_base =3D __DECONST(vo=
id *,
ptr);

The only file above involved in common for this context turns out to be:
/usr/src/lib/libc/stdio/printfcommon.h and the above assignment in that fil=
e is
in io_print(struct io_state *iop, const CHAR * __restrict ptr, int len,
locale_t locale), which is not in use for this context. Here is that assign=
ment
anyway (just for reference):

static inline int
io_print(struct io_state *iop, const CHAR * __restrict ptr, int len, locale=
_t
locale)
{

       iop->iov[iop->uio.uio_iovcnt].iov_base =3D (char *)ptr;
       iop->iov[iop->uio.uio_iovcnt].iov_len =3D len;
       iop->uio.uio_resid +=3D len;
. . .

In other words: The segmentation violation is for dereferencing of __vfprin=
tf's
uninitialized io.uio.uio_iov->iov_base .

Returning to tracing the actually used code for this context to support that
claim some more. . .

The __vfprintf (FILE *fp, locale_t locale, const char *fmt0, va_list ap)
eventually does:

       if (io_flush(&io, locale))

and io_flush(struct io_state *iop, locale_t locale) does:

       return (__sprint(iop->fp, &iop->uio, locale));

and _sprintf(FILE *fp, struct __suio *uio, locale_t locale) does:

       err =3D __sfvwrite(fp, uio);

and __sfvwrite(FILE *fp, struct __suio *uio) does:

       p =3D iov->iov_base;
       len =3D iov->iov_len;

where  iov->iov_base is another name for __vfprintf's io.uio.uio_iov->iov_b=
ase
. __sfvwrite then uses:

#define COPY(n)   (void)memcpy((void *)fp->_p, (void *)p, (size_t)(n))

which fails dereferencing p (i.e., dereferencing __vfprintf's
io.uio.uio_iov->iov_base ).=20

In other words (again): The segmentation violation is for dereferencing of =
the
uninitialized __vfprintf io.uio.uio_iov->iov_base unless I've missed some
initialization some place in the executing code for these sorts of "string
output" contexts.

--=20
You are receiving this mail because:
You are the assignee for the bug.=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-206770-8>