Hi,

more is broken in printf(3).

Ingo Schwarze wrote on Fri, Dec 25, 2015 at 12:30:29AM +0100:

> Fourth file, fourth broken file.
> This is the worst bug found so far.
[...]
> When fprintf(fp, "...%ls...", ...) encounters an encoding error,
> it trashes fp BEYOND REPAIR, clearing all FILE flags, making the
> file unreadable, unwriteable, and probably breaking even more than
> that.  Calling clearerr(3) is by far insufficient to undo the
> devastating damage done.

In addition to that, the format string is parsed with mbrtowc(3),
even if PRINTF_WIDE_CHAR is *not* defined, and the failure is *not*
detected.  That is, invalid bytes in the format string (for example,
0xff in an UTF-8 locale or ISO-Latin bytes in the C locale) are
silently treated as the end of the format string, without reporting
an error, only clobbering errno.

In addition to that, __find_arguments() also returns successfully
in this case, and its return value - which can already be -1 on
malloc failure - is never checked.  This can result in access to
invalid memory.

Fix this by always checking the mbrtowc(3) return value against -1,
by failing at once as soon as that happens, and by always checking
__find_arguments() for success.

The diff below includes my previous diff, both are indeed related.

The new chunks are marked with ***NEW***.

OK?
  Ingo


Index: stdio/vfprintf.c
===================================================================
RCS file: /cvs/src/lib/libc/stdio/vfprintf.c,v
retrieving revision 1.69
diff -u -p -r1.69 vfprintf.c
--- stdio/vfprintf.c    29 Sep 2015 03:19:24 -0000      1.69
+++ stdio/vfprintf.c    25 Dec 2015 13:33:57 -0000
@@ -165,10 +165,8 @@ __wcsconv(wchar_t *wcsarg, int prec)
                memset(&mbs, 0, sizeof(mbs));
                p = wcsarg;
                nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
-               if (nbytes == (size_t)-1) {
-                       errno = EILSEQ;
+               if (nbytes == (size_t)-1)
                        return (NULL);
-               }
        } else {
                /*
                 * Optimisation: if the output precision is small enough,
@@ -188,10 +186,8 @@ __wcsconv(wchar_t *wcsarg, int prec)
                                        break;
                                nbytes += clen;
                        }
-                       if (clen == (size_t)-1) {
-                               errno = EILSEQ;
+                       if (clen == (size_t)-1)
                                return (NULL);
-                       }
                }
        }
        if ((convbuf = malloc(nbytes + 1)) == NULL)
@@ -203,7 +199,6 @@ __wcsconv(wchar_t *wcsarg, int prec)
        if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
            nbytes, &mbs)) == (size_t)-1) {
                free(convbuf);
-               errno = EILSEQ;
                return (NULL);
        }
        convbuf[nbytes] = '\0';
@@ -438,7 +433,11 @@ __vfprintf(FILE *fp, const char *fmt0, *** NEW ***
                int hold = nextarg; \
                if (argtable == NULL) { \
                        argtable = statargtable; \
-                       __find_arguments(fmt0, orgap, &argtable, &argtablesiz); 
\
+                       if (__find_arguments(fmt0, orgap, &argtable, \
+                           &argtablesiz) == -1) { \
+                               ret = -1; \
+                               goto error; \
+                       } \
                } \
                nextarg = n2; \
                val = GETARG(int); \
@@ -494,6 +493,10 @@ __vfprintf(FILE *fp, const char *fmt0, *** NEW ***
                                break;
                        }
                }
+               if (n < 0) {
+                       ret = -1;
+                       goto error;
+               }
                if (fmt != cp) {
                        ptrdiff_t m = fmt - cp;
                        if (m < 0 || m > INT_MAX - ret)
@@ -501,7 +504,7 @@ __vfprintf(FILE *fp, const char *fmt0, *** NEW ***
                        PRINT(cp, m);
                        ret += m;
                }
-               if (n <= 0)
+               if (n == 0)
                        goto done;
                fmt++;          /* skip over '%' */
 
@@ -564,8 +567,11 @@ reswitch:  switch (ch) { *** NEW ***
                                nextarg = n;
                                if (argtable == NULL) {
                                        argtable = statargtable;
-                                       __find_arguments(fmt0, orgap,
-                                           &argtable, &argtablesiz);
+                                       if (__find_arguments(fmt0, orgap,
+                                           &argtable, &argtablesiz) == -1) {
+                                               ret = -1;
+                                               goto error;
+                                       }
                                }
                                goto rflag;
                        }
@@ -590,8 +596,11 @@ reswitch:  switch (ch) { *** NEW ***
                                nextarg = n;
                                if (argtable == NULL) {
                                        argtable = statargtable;
-                                       __find_arguments(fmt0, orgap,
-                                           &argtable, &argtablesiz);
+                                       if (__find_arguments(fmt0, orgap,
+                                           &argtable, &argtablesiz) == -1) {
+                                               ret = -1;
+                                               goto error;
+                                       }
                                }
                                goto rflag;
                        }
@@ -640,8 +649,7 @@ reswitch:   switch (ch) {
                                mbseqlen = wcrtomb(buf,
                                    (wchar_t)GETARG(wint_t), &mbs);
                                if (mbseqlen == (size_t)-1) {
-                                       fp->_flags |= __SERR;
-                                       errno = EILSEQ;
+                                       ret = -1;
                                        goto error;
                                }
                                cp = buf;
@@ -855,7 +863,7 @@ fp_common:
                                } else {
                                        convbuf = __wcsconv(wcp, prec);
                                        if (convbuf == NULL) {
-                                               fp->_flags = __SERR;
+                                               ret = -1;
                                                goto error;
                                        }
                                        cp = convbuf;
@@ -1214,7 +1222,9 @@ __find_arguments(const char *fmt0, *** NEW ***
                                break;
                        }
                }
-               if (n <= 0)
+               if (n < 0)
+                       return (-1);
+               if (n == 0)
                        goto done;
                fmt++;          /* skip over '%' */
 

Reply via email to