fix longstanding exit logic bugs in mbsnrtowcs and wcsnrtombs
authorRich Felker <dalias@aerifal.cx>
Wed, 2 May 2012 17:59:48 +0000 (13:59 -0400)
committerRich Felker <dalias@aerifal.cx>
Wed, 2 May 2012 17:59:48 +0000 (13:59 -0400)
these are POSIX 2008 (previously GNU extension) functions that are
rarely used. apparently they had never been tested before, since the
end-of-string logic was completely missing. mbsnrtowcs is used by
modern versions of bash for its glob implementation, and and this bug
was causing tab completion to hang in an infinite loop.

src/multibyte/mbsnrtowcs.c
src/multibyte/wcsnrtombs.c

index c6f0207f27476077e117c2d50729feafd63432ba..f42e30d97126403c431108a8e22354ff804680a0 100644 (file)
@@ -47,6 +47,10 @@ size_t mbsnrtowcs(wchar_t *wcs, const char **src, size_t n, size_t wn, mbstate_t
                                cnt = l;
                                break;
                        }
+                       if (!l) {
+                               s = 0;
+                               break;
+                       }
                        /* have to roll back partial character */
                        *(unsigned *)st = 0;
                        break;
index 666f6f3f20f5928bc158b319c1c635a31645dae0..70b0cacb2b97639d0c4cf28b1f9d5467c2cc0923 100644 (file)
@@ -20,7 +20,7 @@ size_t wcsnrtombs(char *dst, const wchar_t **wcs, size_t wn, size_t n, mbstate_t
        if (!dst) s = buf, n = sizeof buf;
        else s = dst;
 
-       while ( n && ( (n2=wn)>=n || n2>32 ) ) {
+       while ( ws && n && ( (n2=wn)>=n || n2>32 ) ) {
                if (n2>=n) n2=n;
                wn -= n2;
                l = wcsrtombs(s, &ws, n2, 0);
@@ -35,10 +35,11 @@ size_t wcsnrtombs(char *dst, const wchar_t **wcs, size_t wn, size_t n, mbstate_t
                }
                cnt += l;
        }
-       while (n && wn) {
+       if (ws) while (n && wn) {
                l = wcrtomb(s, *ws, 0);
-               if (!(l+1)) {
-                       cnt = l;
+               if ((l+1)<=1) {
+                       if (!l) ws = 0;
+                       else cnt = l;
                        break;
                }
                ws++; wn--;