]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool_doswin: increase allowable length of path sanitizer
authorJay Satiro <raysatiro@yahoo.com>
Sat, 20 Dec 2025 09:08:09 +0000 (04:08 -0500)
committerJay Satiro <raysatiro@yahoo.com>
Tue, 30 Dec 2025 09:00:50 +0000 (04:00 -0500)
- Use 32767-1 instead of PATH_MAX-1 (260-1) as the maximum allowable
  length of a path in Windows.

Prior to this change the path sanitizer in Windows used 32767-1 as the
maximum length only for paths that had the "\\" prefix like
"\\?\longpath". Since then we added some workarounds to open longer
paths without "\\?\" prefix by normalizing the path and adding that
prefix, and the sanitizer is called before the prefix is added.

Bug: https://github.com/curl/curl/issues/20044
Reported-by: Viktor Szakats
Closes https://github.com/curl/curl/pull/20046

src/tool_doswin.c
tests/tunit/tool1604.c

index 36145bc4b4a4a1616bbee38961afc7640d7d36f4..034462b3847246c3c84327dce9e85e4beb90a786 100644 (file)
@@ -88,8 +88,11 @@ SANITIZE_ALLOW_PATH:       Allow path separators and colons.
 Without this flag path separators and colons are sanitized.
 
 SANITIZE_ALLOW_RESERVED:   Allow reserved device names.
-Without this flag a reserved device name is renamed (COM1 => _COM1) unless it
-is in a UNC prefixed path.
+Without this flag a reserved device name is renamed (COM1 => _COM1).
+
+To fully block reserved device names requires not passing either flag. Some
+less common path styles are allowed to use reserved device names. For example,
+a "\\" prefixed path may use reserved device names if paths are allowed.
 
 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
@@ -110,15 +113,13 @@ SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name,
   if(!file_name)
     return SANITIZE_ERR_BAD_ARGUMENT;
 
-  if(flags & SANITIZE_ALLOW_PATH) {
-#ifndef MSDOS
-    if(file_name[0] == '\\' && file_name[1] == '\\')
-      /* UNC prefixed path \\ (eg \\?\C:\foo) */
-      max_sanitized_len = 32767 - 1;
-    else
+  if(flags & SANITIZE_ALLOW_PATH)
+#ifdef MSDOS
+    max_sanitized_len = PATH_MAX - 1;
+#else
+    /* Windows extended-length path max */
+    max_sanitized_len = 32767 - 1;
 #endif
-      max_sanitized_len = PATH_MAX - 1;
-  }
   else
     /* The maximum length of a filename. FILENAME_MAX is often the same as
        PATH_MAX, in other words it is 260 and does not discount the path
@@ -135,7 +136,7 @@ SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name,
 
 #ifndef MSDOS
   if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
-    /* Skip the literal path prefix \\?\ */
+    /* Skip the literal-path prefix \\?\ */
     p = target + 4;
   else
 #endif
@@ -438,20 +439,18 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized,
   /* We could have a file whose name is a device on MS-DOS. Trying to
    * retrieve such a file would fail at best and wedge us at worst. We need
    * to rename such files. */
-  char *p, *base;
-  char fname[PATH_MAX];
+  char *p, *base, *buffer;
 #ifdef MSDOS
   struct_stat st_buf;
 #endif
-  size_t len;
+  size_t len, bufsize;
 
   if(!sanitized || !file_name)
     return SANITIZE_ERR_BAD_ARGUMENT;
 
   *sanitized = NULL;
-  len = strlen(file_name);
 
-  /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
+  /* Ignore "\\" prefixed paths, they are allowed to use reserved names. */
 #ifndef MSDOS
   if((flags & SANITIZE_ALLOW_PATH) &&
      file_name[0] == '\\' && file_name[1] == '\\') {
@@ -462,19 +461,25 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized,
   }
 #endif
 
-  if(len > PATH_MAX - 1)
-    return SANITIZE_ERR_INVALID_PATH;
+  /* The buffer contains two extra bytes to allow for path expansion that
+     occurs if reserved name(s) need an underscore prepended. */
+  len = strlen(file_name);
+  bufsize = len + 2 + 1;
+
+  buffer = curlx_malloc(bufsize);
+  if(!buffer)
+    return SANITIZE_ERR_OUT_OF_MEMORY;
+
+  memcpy(buffer, file_name, len + 1);
 
-  memcpy(fname, file_name, len);
-  fname[len] = '\0';
-  base = basename(fname);
+  base = basename(buffer);
 
   /* Rename reserved device names that are known to be accessible without \\.\
      Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS
      https://web.archive.org/web/20160314141551/support.microsoft.com/en-us/kb/74496
      https://learn.microsoft.com/windows/win32/fileio/naming-a-file
      */
-  for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
+  for(p = buffer; p; p = (p == buffer && buffer != base ? base : NULL)) {
     size_t p_len;
     int x = (curl_strnequal(p, "CON", 3) ||
              curl_strnequal(p, "PRN", 3) ||
@@ -511,15 +516,18 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized,
     p_len = strlen(p);
 
     /* Prepend a '_' */
-    if(strlen(fname) == PATH_MAX - 1)
+    if(len == bufsize - 1) {
+      curlx_free(buffer);
       return SANITIZE_ERR_INVALID_PATH;
+    }
     memmove(p + 1, p, p_len + 1);
     p[0] = '_';
     ++p_len;
+    ++len;
 
-    /* if fname was just modified then the basename pointer must be updated */
-    if(p == fname)
-      base = basename(fname);
+    /* the basename pointer must be updated since the path has expanded */
+    if(p == buffer)
+      base = basename(buffer);
   }
 
   /* This is the legacy portion from rename_if_dos_device_name that checks for
@@ -534,16 +542,19 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized,
     /* Prepend a '_' */
     size_t blen = strlen(base);
     if(blen) {
-      if(strlen(fname) >= PATH_MAX - 1)
+      if(len == bufsize - 1) {
+        curlx_free(buffer);
         return SANITIZE_ERR_INVALID_PATH;
+      }
       memmove(base + 1, base, blen + 1);
       base[0] = '_';
+      ++len;
     }
   }
 #endif
 
-  *sanitized = curlx_strdup(fname);
-  return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY;
+  *sanitized = buffer;
+  return SANITIZE_ERR_OK;
 }
 
 #ifdef __DJGPP__
index dcfee4cbac62e71b12ed84819903d40e7cda19a3..de665bbc5e9c16c341d019f30b7c56c77d31e87c 100644 (file)
@@ -178,9 +178,6 @@ static CURLcode test_tool1604(const char *arg)
     { "COM56", 0,
       "COM56", SANITIZE_ERR_OK
     },
-    /* At the moment we expect a maximum path length of 259. I assume MS-DOS
-       has variable max path lengths depending on compiler that are shorter
-       so currently these "good" truncate tests will not run on MS-DOS */
     { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
       "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
       "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"