From: Jay Satiro Date: Sat, 20 Dec 2025 09:08:09 +0000 (-0500) Subject: tool_doswin: increase allowable length of path sanitizer X-Git-Tag: curl-8_18_0~74 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1068d048ec85befdaa88b9cb868d4255714aa9cc;p=thirdparty%2Fcurl.git tool_doswin: increase allowable length of path sanitizer - 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 --- diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 36145bc4b4..034462b384 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -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__ diff --git a/tests/tunit/tool1604.c b/tests/tunit/tool1604.c index dcfee4cbac..de665bbc5e 100644 --- a/tests/tunit/tool1604.c +++ b/tests/tunit/tool1604.c @@ -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"