The SWI-Prolog GIT repositories

SECURITY: Possible buffer overflows when expanding file-names with long
authorJan Wielemaker <J.Wielemaker@cs.vu.nl>
Sun, 16 Dec 2012 16:29:37 +0000 (17:29 +0100)
committerJan Wielemaker <J.Wielemaker@cs.vu.nl>
Sun, 16 Dec 2012 20:59:30 +0000 (21:59 +0100)
paths.  Affects expand_file_name/2.

Can lead to crashes (DoS attacks) and possibly execution of arbitrary
code if an attacker can control the names of the files searched for,
e.g., if expand_file_name/2 is used in a directory to which an attacker
can upload files for which he can control the name.

src/os/pl-glob.c

index 46ddf5b..0b1baeb 100644 (file)
@@ -424,6 +424,7 @@ expand(const char *pattern, GlobInfo info)
   compiled_pattern cbuf;
   char prefix[MAXPATHLEN];             /* before first pattern */
   char patbuf[MAXPATHLEN];             /* pattern buffer */
+  size_t prefix_len;
   int end, dot;
 
   initBuffer(&info->files);
@@ -442,20 +443,25 @@ expand(const char *pattern, GlobInfo info)
       switch( (c=*s++) )
       { case EOS:
          if ( s > pat )                /* something left and expanded */
-         { un_escape(prefix, pat, s);
+         { size_t prefix_len;
+
+           un_escape(prefix, pat, s);
+           prefix_len = strlen(prefix);
 
            end = info->end;
            for( ; info->start < end; info->start++ )
            { char path[MAXPATHLEN];
-             size_t plen;
-
-             strcpy(path, expand_entry(info, info->start));
-             plen = strlen(path);
-             if ( prefix[0] && plen > 0 && path[plen-1] != '/' )
-               path[plen++] = '/';
-             strcpy(&path[plen], prefix);
-             if ( end == 1 || AccessFile(path, ACCESS_EXIST) )
-               add_path(path, info);
+             const char *entry = expand_entry(info, info->start);
+             size_t plen = strlen(entry);
+
+             if ( plen+prefix_len+2 <= MAXPATHLEN )
+             { strcpy(path, entry);
+               if ( prefix[0] && plen > 0 && path[plen-1] != '/' )
+                 path[plen++] = '/';
+               strcpy(&path[plen], prefix);
+               if ( end == 1 || AccessFile(path, ACCESS_EXIST) )
+                 add_path(path, info);
+             }
            }
          }
          succeed;
@@ -490,8 +496,9 @@ expand(const char *pattern, GlobInfo info)
 */
     un_escape(prefix, pat, head);
     un_escape(patbuf, head, tail);
+    prefix_len = strlen(prefix);
 
-    if ( !compilePattern(patbuf, &cbuf) )              /* syntax error */
+    if ( !compilePattern(patbuf, &cbuf) )      /* syntax error */
       fail;
     dot = (patbuf[0] == '.');                  /* do dots as well */
 
@@ -503,12 +510,16 @@ expand(const char *pattern, GlobInfo info)
       char path[MAXPATHLEN];
       char tmp[MAXPATHLEN];
       const char *current = expand_entry(info, info->start);
+      size_t clen = strlen(current);
+
+      if ( clen+prefix_len+1 > sizeof(path) )
+       continue;
 
       strcpy(path, current);
-      strcat(path, prefix);
+      strcpy(&path[clen], prefix);
 
       if ( (d=opendir(path[0] ? OsPath(path, tmp) : ".")) )
-      { size_t plen = strlen(path);
+      { size_t plen = clen+prefix_len;
 
        if ( plen > 0 && path[plen-1] != '/' )
          path[plen++] = '/';
@@ -522,12 +533,11 @@ expand(const char *pattern, GlobInfo info)
               matchPattern(e->d_name, &cbuf) )
          { char newp[MAXPATHLEN];
 
-           strcpy(newp, path);
-           strcpy(&newp[plen], e->d_name);
-/*         if ( !tail[0] || ExistsDirectory(newp) )
-           Saves memory, but involves one more file-access
-*/
+           if ( plen+strlen(e->d_name)+1 < sizeof(newp) )
+           { strcpy(newp, path);
+             strcpy(&newp[plen], e->d_name);
              add_path(newp, info);
+           }
          }
        }
        closedir(d);

Further information about the SWI-Prolog GIT repositories