bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug-gnulib] fnmatch fixes to avoid alloca overflow, address arithmetic


From: Paul Eggert
Subject: [Bug-gnulib] fnmatch fixes to avoid alloca overflow, address arithmetic overflow
Date: 15 Oct 2003 23:55:33 -0700
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3

I installed the following patch to fix address arithmetic overflow
bugs in gnulib's copy of fnmatch.  This stuff should be migrated back
into libc at some point, of course.

2003-10-15  Paul Eggert  <address@hidden>

        * fnmatch.c (SIZE_MAX): Define if standard headers don't.
        (fnmatch): Do not alloca more than 2000 wide characters;
        instead, use malloc for large buffers.
        Check for address arithmetic overflow, and return -1
        with errno set to ENOMEM in that case.
        * fnmatch_loop.c (ALLOCA_LIMIT): New macro.
        (NEW_PATTERN): Do not alloca more than 8000 bytes;
        instead, return -1.  Check for address arithmetic overflow.

Index: lib/fnmatch.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/fnmatch.c,v
retrieving revision 1.24
diff -p -u -r1.24 fnmatch.c
--- lib/fnmatch.c       9 Sep 2003 06:48:57 -0000       1.24
+++ lib/fnmatch.c       16 Oct 2003 06:46:27 -0000
@@ -80,6 +80,10 @@ char *alloca ();
 extern int fnmatch (const char *pattern, const char *string, int flags);
 #endif
 
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
 /* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set.  */
 #define NO_LEADING_PERIOD(flags) \
   ((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD))
@@ -318,40 +322,70 @@ int
 fnmatch (const char *pattern, const char *string, int flags)
 {
 # if HANDLE_MULTIBYTE
+#  define ALLOCA_LIMIT 2000
   if (__builtin_expect (MB_CUR_MAX, 1) != 1)
     {
       mbstate_t ps;
-      size_t n;
+      size_t patsize;
+      size_t strsize;
+      size_t totsize;
       wchar_t *wpattern;
       wchar_t *wstring;
+      int res;
 
-      /* Convert the strings into wide characters.  */
+      /* Calculate the size needed to convert the strings to
+        wide characters.  */
       memset (&ps, '\0', sizeof (ps));
-      n = mbsrtowcs (NULL, &pattern, 0, &ps);
-      if (__builtin_expect (n, 0) == (size_t) -1)
+      patsize = mbsrtowcs (NULL, &pattern, 0, &ps) + 1;
+      if (__builtin_expect (patsize == 0, 0))
        /* Something wrong.
           XXX Do we have to set `errno' to something which mbsrtows hasn't
           already done?  */
        return -1;
-      wpattern = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
-      assert (mbsinit (&ps));
-      (void) mbsrtowcs (wpattern, &pattern, n + 1, &ps);
-
       assert (mbsinit (&ps));
-      n = mbsrtowcs (NULL, &string, 0, &ps);
-      if (__builtin_expect (n, 0) == (size_t) -1)
+      strsize = mbsrtowcs (NULL, &string, 0, &ps) + 1;
+      if (__builtin_expect (strsize == 0, 0))
        /* Something wrong.
           XXX Do we have to set `errno' to something which mbsrtows hasn't
           already done?  */
        return -1;
-      wstring = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
       assert (mbsinit (&ps));
-      (void) mbsrtowcs (wstring, &string, n + 1, &ps);
+      totsize = patsize + strsize;
+      if (__builtin_expect (! (patsize <= totsize
+                              && totsize <= SIZE_MAX / sizeof (wchar_t)),
+                           0))
+       {
+         errno = ENOMEM;
+         return -1;
+       }
+
+      /* Allocate room for the wide characters.  */
+      if (__builtin_expect (totsize < ALLOCA_LIMIT, 1))
+       wpattern = (wchar_t *) alloca (totsize * sizeof (wchar_t));
+      else
+       {
+         wpattern = malloc (totsize * sizeof (wchar_t));
+         if (__builtin_expect (! wpattern, 0))
+           {
+             errno = ENOMEM;
+             return -1;
+           }
+       }
+      wstring = wpattern + patsize;
+
+      /* Convert the strings into wide characters.  */
+      mbsrtowcs (wpattern, &pattern, patsize, &ps);
+      assert (mbsinit (&ps));
+      mbsrtowcs (wstring, &string, strsize, &ps);
+
+      res = internal_fnwmatch (wpattern, wstring, wstring + strsize - 1,
+                              flags & FNM_PERIOD, flags);
 
-      return internal_fnwmatch (wpattern, wstring, wstring + n,
-                               flags & FNM_PERIOD, flags);
+      if (__builtin_expect (! (totsize < ALLOCA_LIMIT), 0))
+       free (wpattern);
+      return res;
     }
-# endif  /* mbstate_t and mbsrtowcs or _LIBC.  */
+# endif /* HANDLE_MULTIBYTE */
 
   return internal_fnmatch (pattern, string, string + strlen (string),
                           flags & FNM_PERIOD, flags);
Index: lib/fnmatch_loop.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/fnmatch_loop.c,v
retrieving revision 1.4
diff -p -u -r1.4 fnmatch_loop.c
--- lib/fnmatch_loop.c  9 Sep 2003 06:48:57 -0000       1.4
+++ lib/fnmatch_loop.c  16 Oct 2003 06:46:27 -0000
@@ -1042,16 +1042,23 @@ EXT (INT opt, const CHAR *pattern, const
        if (level-- == 0)
          {
            /* This means we found the end of the pattern.  */
+#define ALLOCA_LIMIT 8000
 #define NEW_PATTERN \
            struct patternlist *newp;                                         \
            size_t plen;                                                      \
+           size_t plensize;                                                  \
+           size_t newpsize;                                                  \
                                                                              \
            plen = (opt == L('?') || opt == L('@')                            \
                    ? pattern_len                                             \
                    : p - startp + 1);                                        \
-           newp = (struct patternlist *)                                     \
-             alloca (offsetof (struct patternlist, str)                      \
-                     + (plen * sizeof (CHAR)));                              \
+           plensize = plen * sizeof (CHAR);                                  \
+           newpsize = offsetof (struct patternlist, str) + plensize;         \
+           if ((size_t) -1 / sizeof (CHAR) < plen                            \
+               || newpsize < offsetof (struct patternlist, str)              \
+               || ALLOCA_LIMIT <= newpsize)                                  \
+             return -1;                                                      \
+           newp = (struct patternlist *) alloca (newpsize);                  \
            *((CHAR *) MEMPCPY (newp->str, startp, p - startp)) = L('\0');    \
            newp->next = NULL;                                                \
            *lastp = newp;                                                    \




reply via email to

[Prev in Thread] Current Thread [Next in Thread]