[Bug 206770] 11.0-CURRENT/clang380-import: libc/stdio uninitialized pointer use (exposed via powerpc 32-bit context)

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Sun Jan 31 01:50:55 UTC 2016


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=206770

            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 at FreeBSD.org
          Reporter: markmi at 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 central
point is __vfprintf and what it does/calls for such "string output" routines.

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 involved.

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

#0  0x419a89c8 in memcpy (dst0=0xffffd734, src0=<optimized out>,
length=<optimized out>) at /usr/src/lib/libc/string/bcopy.c:124
124                             TLOOP1(*--dst = *--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 = FAKE_FILE;
. . .
       va_start(ap, fmt);
       f._flags = __SWR | __SSTR;
       f._bf._base = f._p = (unsigned char *)str;
       f._bf._size = f._w = n;
       ret = __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 = iop->iov;
       iop->uio.uio_resid = 0;
       iop->uio.uio_iovcnt = 0;
       iop->fp = 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 to.

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_base
(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.*=" /usr/src/lib/libc/stdio/*
/usr/src/lib/libc/stdio/fputs.c:        iov.iov_base = (void *)s;
/usr/src/lib/libc/stdio/fputws.c:       iov.iov_base = buf;
/usr/src/lib/libc/stdio/fwrite.c:       iov.iov_base = (void *)buf;
/usr/src/lib/libc/stdio/perror.c:               v->iov_base = (char *)s;
/usr/src/lib/libc/stdio/perror.c:               v->iov_base = ": ";
/usr/src/lib/libc/stdio/perror.c:       v->iov_base = msgbuf;
/usr/src/lib/libc/stdio/perror.c:       v->iov_base = "\n";
/usr/src/lib/libc/stdio/printfcommon.h: iop->iov[iop->uio.uio_iovcnt].iov_base
= (char *)ptr;
/usr/src/lib/libc/stdio/puts.c: iov[0].iov_base = (void *)s;
/usr/src/lib/libc/stdio/puts.c: iov[1].iov_base = "\n";
/usr/src/lib/libc/stdio/putw.c: iov.iov_base = &w;
/usr/src/lib/libc/stdio/vfwprintf.c:    iov.iov_base = buf;
/usr/src/lib/libc/stdio/xprintf.c:      io->iovp->iov_base = __DECONST(void *,
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 file 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 assignment
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 = (char *)ptr;
       iop->iov[iop->uio.uio_iovcnt].iov_len = len;
       iop->uio.uio_resid += len;
. . .

In other words: The segmentation violation is for dereferencing of __vfprintf'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 = __sfvwrite(fp, uio);

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

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

where  iov->iov_base is another name for __vfprintf's io.uio.uio_iov->iov_base
. __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 ). 

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.

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


More information about the freebsd-bugs mailing list