[Python-checkins] bpo-36352: Avoid hardcoded MAXPATHLEN size in getpath.c (GH-12423)

Victor Stinner webhook-mailer at python.org
Mon Mar 18 21:58:17 EDT 2019


https://github.com/python/cpython/commit/faddaedd05ca81a9fed3f315e7bc8dcf455824a2
commit: faddaedd05ca81a9fed3f315e7bc8dcf455824a2
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-03-19T02:58:14+01:00
summary:

bpo-36352: Avoid hardcoded MAXPATHLEN size in getpath.c (GH-12423)

* Use Py_ARRAY_LENGTH() rather than hardcoded MAXPATHLEN in getpath.c.
* Pass string length to functions modifying strings.

files:
A Misc/NEWS.d/next/Core and Builtins/2019-03-19-02-36-40.bpo-36352.qj2trz.rst
M Modules/getpath.c
M Python/fileutils.c
M Python/pathconfig.c

diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-03-19-02-36-40.bpo-36352.qj2trz.rst b/Misc/NEWS.d/next/Core and Builtins/2019-03-19-02-36-40.bpo-36352.qj2trz.rst
new file mode 100644
index 000000000000..148201cd1b6e
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-03-19-02-36-40.bpo-36352.qj2trz.rst	
@@ -0,0 +1,2 @@
+Python initialization now fails with an error, rather than silently
+truncating paths, if a path is too long.
diff --git a/Modules/getpath.c b/Modules/getpath.c
index 436431749494..dd188c612893 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -159,14 +159,16 @@ static void
 reduce(wchar_t *dir)
 {
     size_t i = wcslen(dir);
-    while (i > 0 && dir[i] != SEP)
+    while (i > 0 && dir[i] != SEP) {
         --i;
+    }
     dir[i] = '\0';
 }
 
 
+/* Is file, not directory */
 static int
-isfile(wchar_t *filename)          /* Is file, not directory */
+isfile(const wchar_t *filename)
 {
     struct stat buf;
     if (_Py_wstat(filename, &buf) != 0) {
@@ -179,15 +181,16 @@ isfile(wchar_t *filename)          /* Is file, not directory */
 }
 
 
+/* Is module -- check for .pyc too */
 static int
-ismodule(wchar_t *filename)        /* Is module -- check for .pyc too */
+ismodule(wchar_t *filename, size_t filename_len)
 {
     if (isfile(filename)) {
         return 1;
     }
 
     /* Check for the compiled version of prefix. */
-    if (wcslen(filename) < MAXPATHLEN) {
+    if (wcslen(filename) + 2 <= filename_len) {
         wcscat(filename, L"c");
         if (isfile(filename)) {
             return 1;
@@ -199,7 +202,7 @@ ismodule(wchar_t *filename)        /* Is module -- check for .pyc too */
 
 /* Is executable file */
 static int
-isxfile(wchar_t *filename)
+isxfile(const wchar_t *filename)
 {
     struct stat buf;
     if (_Py_wstat(filename, &buf) != 0) {
@@ -231,58 +234,71 @@ isdir(wchar_t *filename)
 
 
 /* Add a path component, by appending stuff to buffer.
-   buffer must have at least MAXPATHLEN + 1 bytes allocated, and contain a
-   NUL-terminated string with no more than MAXPATHLEN characters (not counting
-   the trailing NUL).  It's a fatal error if it contains a string longer than
-   that (callers must be careful!).  If these requirements are met, it's
-   guaranteed that buffer will still be a NUL-terminated string with no more
-   than MAXPATHLEN characters at exit.  If stuff is too long, only as much of
-   stuff as fits will be appended.
-*/
+   buflen: 'buffer' length in characters including trailing NUL. */
 static _PyInitError
-joinpath(wchar_t *buffer, wchar_t *stuff)
+joinpath(wchar_t *buffer, const wchar_t *stuff, size_t buflen)
 {
     size_t n, k;
-    if (stuff[0] == SEP) {
-        n = 0;
-    }
-    else {
+    if (stuff[0] != SEP) {
         n = wcslen(buffer);
-        if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) {
+        if (n >= buflen) {
+            return PATHLEN_ERR();
+        }
+
+        if (n > 0 && buffer[n-1] != SEP) {
             buffer[n++] = SEP;
         }
     }
-    if (n > MAXPATHLEN) {
-        return PATHLEN_ERR();
+    else {
+        n = 0;
     }
+
     k = wcslen(stuff);
-    if (n + k > MAXPATHLEN) {
-        k = MAXPATHLEN - n;
+    if (n + k >= buflen) {
+        return PATHLEN_ERR();
     }
     wcsncpy(buffer+n, stuff, k);
     buffer[n+k] = '\0';
+
     return _Py_INIT_OK();
 }
 
 
+static inline int
+safe_wcscpy(wchar_t *dst, const wchar_t *src, size_t n)
+{
+    size_t srclen = wcslen(src);
+    if (n <= srclen) {
+        dst[0] = L'\0';
+        return -1;
+    }
+    memcpy(dst, src, (srclen + 1) * sizeof(wchar_t));
+    return 0;
+}
+
+
 /* copy_absolute requires that path be allocated at least
-   MAXPATHLEN + 1 bytes and that p be no more than MAXPATHLEN bytes. */
+   'pathlen' characters (including trailing NUL). */
 static _PyInitError
-copy_absolute(wchar_t *path, wchar_t *p, size_t pathlen)
+copy_absolute(wchar_t *path, const wchar_t *p, size_t pathlen)
 {
     if (p[0] == SEP) {
-        wcscpy(path, p);
+        if (safe_wcscpy(path, p, pathlen) < 0) {
+            return PATHLEN_ERR();
+        }
     }
     else {
         if (!_Py_wgetcwd(path, pathlen)) {
             /* unable to get the current directory */
-            wcscpy(path, p);
+            if (safe_wcscpy(path, p, pathlen) < 0) {
+                return PATHLEN_ERR();
+            }
             return _Py_INIT_OK();
         }
         if (p[0] == '.' && p[1] == SEP) {
             p += 2;
         }
-        _PyInitError err = joinpath(path, p);
+        _PyInitError err = joinpath(path, p, pathlen);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -291,56 +307,54 @@ copy_absolute(wchar_t *path, wchar_t *p, size_t pathlen)
 }
 
 
-/* absolutize() requires that path be allocated at least MAXPATHLEN+1 bytes. */
+/* path_len: path length in characters including trailing NUL */
 static _PyInitError
-absolutize(wchar_t *path)
+absolutize(wchar_t *path, size_t path_len)
 {
-    wchar_t buffer[MAXPATHLEN+1];
-
     if (path[0] == SEP) {
         return _Py_INIT_OK();
     }
 
-    _PyInitError err = copy_absolute(buffer, path, MAXPATHLEN+1);
+    wchar_t abs_path[MAXPATHLEN+1];
+    _PyInitError err = copy_absolute(abs_path, path, Py_ARRAY_LENGTH(abs_path));
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
-    wcscpy(path, buffer);
+
+    if (safe_wcscpy(path, abs_path, path_len) < 0) {
+        return PATHLEN_ERR();
+    }
     return _Py_INIT_OK();
 }
 
 
 #if defined(__CYGWIN__) || defined(__MINGW32__)
-/* add_exe_suffix requires that progpath be allocated at least
-   MAXPATHLEN + 1 bytes.
-*/
-
 #ifndef EXE_SUFFIX
 #define EXE_SUFFIX L".exe"
 #endif
 
+/* pathlen: 'path' length in characters including trailing NUL */
 static _PyInitError
-add_exe_suffix(wchar_t *progpath)
+add_exe_suffix(wchar_t *progpath, size_t progpathlen)
 {
     /* Check for already have an executable suffix */
     size_t n = wcslen(progpath);
     size_t s = wcslen(EXE_SUFFIX);
-    if (wcsncasecmp(EXE_SUFFIX, progpath+n-s, s) != 0) {
-        if (n + s > MAXPATHLEN) {
-            return PATHLEN_ERR();
-        }
-        /* Save original path for revert */
-        wchar_t orig[MAXPATHLEN+1];
-        wcsncpy(orig, progpath, MAXPATHLEN);
+    if (wcsncasecmp(EXE_SUFFIX, progpath + n - s, s) == 0) {
+        return _Py_INIT_OK();
+    }
 
-        wcsncpy(progpath+n, EXE_SUFFIX, s);
-        progpath[n+s] = '\0';
+    if (n + s >= progpathlen) {
+        return PATHLEN_ERR();
+    }
+    wcsncpy(progpath + n, EXE_SUFFIX, s);
+    progpath[n+s] = '\0';
 
-        if (!isxfile(progpath)) {
-            /* Path that added suffix is invalid */
-            wcsncpy(progpath, orig, MAXPATHLEN);
-        }
+    if (!isxfile(progpath)) {
+        /* Path that added suffix is invalid: truncate (remove suffix) */
+        progpath[n] = '\0';
     }
+
     return _Py_INIT_OK();
 }
 #endif
@@ -350,8 +364,9 @@ add_exe_suffix(wchar_t *progpath)
    bytes long.
 */
 static _PyInitError
-search_for_prefix(const _PyCoreConfig *core_config,
-                  PyCalculatePath *calculate, wchar_t *prefix, int *found)
+search_for_prefix(const _PyCoreConfig *core_config, PyCalculatePath *calculate,
+                  wchar_t *prefix, size_t prefix_len,
+                  int *found)
 {
     _PyInitError err;
     size_t n;
@@ -359,17 +374,18 @@ search_for_prefix(const _PyCoreConfig *core_config,
 
     /* If PYTHONHOME is set, we believe it unconditionally */
     if (core_config->home) {
-        wcsncpy(prefix, core_config->home, MAXPATHLEN);
-        prefix[MAXPATHLEN] = L'\0';
+        if (safe_wcscpy(prefix, core_config->home, prefix_len) < 0) {
+            return PATHLEN_ERR();
+        }
         wchar_t *delim = wcschr(prefix, DELIM);
         if (delim) {
             *delim = L'\0';
         }
-        err = joinpath(prefix, calculate->lib_python);
+        err = joinpath(prefix, calculate->lib_python, prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
-        err = joinpath(prefix, LANDMARK);
+        err = joinpath(prefix, LANDMARK, prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -378,9 +394,10 @@ search_for_prefix(const _PyCoreConfig *core_config,
     }
 
     /* Check to see if argv[0] is in the build directory */
-    wcsncpy(prefix, calculate->argv0_path, MAXPATHLEN);
-    prefix[MAXPATHLEN] = L'\0';
-    err = joinpath(prefix, L"Modules/Setup.local");
+    if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) {
+        return PATHLEN_ERR();
+    }
+    err = joinpath(prefix, L"Modules/Setup.local", prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -389,24 +406,25 @@ search_for_prefix(const _PyCoreConfig *core_config,
         /* Check VPATH to see if argv0_path is in the build directory. */
         vpath = Py_DecodeLocale(VPATH, NULL);
         if (vpath != NULL) {
-            wcsncpy(prefix, calculate->argv0_path, MAXPATHLEN);
-            prefix[MAXPATHLEN] = L'\0';
-            err = joinpath(prefix, vpath);
+            if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) {
+                return PATHLEN_ERR();
+            }
+            err = joinpath(prefix, vpath, prefix_len);
             PyMem_RawFree(vpath);
             if (_Py_INIT_FAILED(err)) {
                 return err;
             }
 
-            err = joinpath(prefix, L"Lib");
+            err = joinpath(prefix, L"Lib", prefix_len);
             if (_Py_INIT_FAILED(err)) {
                 return err;
             }
-            err = joinpath(prefix, LANDMARK);
+            err = joinpath(prefix, LANDMARK, prefix_len);
             if (_Py_INIT_FAILED(err)) {
                 return err;
             }
 
-            if (ismodule(prefix)) {
+            if (ismodule(prefix, prefix_len)) {
                 *found = -1;
                 return _Py_INIT_OK();
             }
@@ -414,23 +432,23 @@ search_for_prefix(const _PyCoreConfig *core_config,
     }
 
     /* Search from argv0_path, until root is found */
-    err = copy_absolute(prefix, calculate->argv0_path, MAXPATHLEN+1);
+    err = copy_absolute(prefix, calculate->argv0_path, prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
 
     do {
         n = wcslen(prefix);
-        err = joinpath(prefix, calculate->lib_python);
+        err = joinpath(prefix, calculate->lib_python, prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
-        err = joinpath(prefix, LANDMARK);
+        err = joinpath(prefix, LANDMARK, prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
 
-        if (ismodule(prefix)) {
+        if (ismodule(prefix, prefix_len)) {
             *found = 1;
             return _Py_INIT_OK();
         }
@@ -439,18 +457,19 @@ search_for_prefix(const _PyCoreConfig *core_config,
     } while (prefix[0]);
 
     /* Look at configure's PREFIX */
-    wcsncpy(prefix, calculate->prefix, MAXPATHLEN);
-    prefix[MAXPATHLEN] = L'\0';
-    err = joinpath(prefix, calculate->lib_python);
+    if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) {
+        return PATHLEN_ERR();
+    }
+    err = joinpath(prefix, calculate->lib_python, prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
-    err = joinpath(prefix, LANDMARK);
+    err = joinpath(prefix, LANDMARK, prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
 
-    if (ismodule(prefix)) {
+    if (ismodule(prefix, prefix_len)) {
         *found = 1;
         return _Py_INIT_OK();
     }
@@ -463,11 +482,12 @@ search_for_prefix(const _PyCoreConfig *core_config,
 
 static _PyInitError
 calculate_prefix(const _PyCoreConfig *core_config,
-                 PyCalculatePath *calculate, wchar_t *prefix)
+                 PyCalculatePath *calculate, wchar_t *prefix, size_t prefix_len)
 {
     _PyInitError err;
 
-    err = search_for_prefix(core_config, calculate, prefix, &calculate->prefix_found);
+    err = search_for_prefix(core_config, calculate, prefix, prefix_len,
+                            &calculate->prefix_found);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -477,8 +497,10 @@ calculate_prefix(const _PyCoreConfig *core_config,
             fprintf(stderr,
                 "Could not find platform independent libraries <prefix>\n");
         }
-        wcsncpy(prefix, calculate->prefix, MAXPATHLEN);
-        err = joinpath(prefix, calculate->lib_python);
+        if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) {
+            return PATHLEN_ERR();
+        }
+        err = joinpath(prefix, calculate->lib_python, prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -490,8 +512,9 @@ calculate_prefix(const _PyCoreConfig *core_config,
 }
 
 
-static void
-calculate_reduce_prefix(PyCalculatePath *calculate, wchar_t *prefix)
+static _PyInitError
+calculate_reduce_prefix(PyCalculatePath *calculate,
+                        wchar_t *prefix, size_t prefix_len)
 {
     /* Reduce prefix and exec_prefix to their essence,
      * e.g. /usr/local/lib/python1.5 is reduced to /usr/local.
@@ -508,8 +531,11 @@ calculate_reduce_prefix(PyCalculatePath *calculate, wchar_t *prefix)
         }
     }
     else {
-        wcsncpy(prefix, calculate->prefix, MAXPATHLEN);
+        if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) {
+            return PATHLEN_ERR();
+        }
     }
+    return _Py_INIT_OK();
 }
 
 
@@ -518,7 +544,8 @@ calculate_reduce_prefix(PyCalculatePath *calculate, wchar_t *prefix)
 */
 static _PyInitError
 search_for_exec_prefix(const _PyCoreConfig *core_config,
-                       PyCalculatePath *calculate, wchar_t *exec_prefix,
+                       PyCalculatePath *calculate,
+                       wchar_t *exec_prefix, size_t exec_prefix_len,
                        int *found)
 {
     _PyInitError err;
@@ -528,17 +555,20 @@ search_for_exec_prefix(const _PyCoreConfig *core_config,
     if (core_config->home) {
         wchar_t *delim = wcschr(core_config->home, DELIM);
         if (delim) {
-            wcsncpy(exec_prefix, delim+1, MAXPATHLEN);
+            if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) {
+                return PATHLEN_ERR();
+            }
         }
         else {
-            wcsncpy(exec_prefix, core_config->home, MAXPATHLEN);
+            if (safe_wcscpy(exec_prefix, core_config->home, exec_prefix_len) < 0) {
+                return PATHLEN_ERR();
+            }
         }
-        exec_prefix[MAXPATHLEN] = L'\0';
-        err = joinpath(exec_prefix, calculate->lib_python);
+        err = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
-        err = joinpath(exec_prefix, L"lib-dynload");
+        err = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -549,9 +579,10 @@ search_for_exec_prefix(const _PyCoreConfig *core_config,
     /* Check to see if argv[0] is in the build directory. "pybuilddir.txt"
        is written by setup.py and contains the relative path to the location
        of shared library modules. */
-    wcsncpy(exec_prefix, calculate->argv0_path, MAXPATHLEN);
-    exec_prefix[MAXPATHLEN] = L'\0';
-    err = joinpath(exec_prefix, L"pybuilddir.txt");
+    if (safe_wcscpy(exec_prefix, calculate->argv0_path, exec_prefix_len) < 0) {
+        return PATHLEN_ERR();
+    }
+    err = joinpath(exec_prefix, L"pybuilddir.txt", exec_prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -562,21 +593,22 @@ search_for_exec_prefix(const _PyCoreConfig *core_config,
             errno = 0;
         }
         else {
-            char buf[MAXPATHLEN+1];
-            n = fread(buf, 1, MAXPATHLEN, f);
+            char buf[MAXPATHLEN + 1];
+            n = fread(buf, 1, Py_ARRAY_LENGTH(buf) - 1, f);
             buf[n] = '\0';
             fclose(f);
 
-            size_t dec_len;
             wchar_t *pybuilddir;
+            size_t dec_len;
             pybuilddir = _Py_DecodeUTF8_surrogateescape(buf, n, &dec_len);
             if (!pybuilddir) {
                 return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len);
             }
 
-            wcsncpy(exec_prefix, calculate->argv0_path, MAXPATHLEN);
-            exec_prefix[MAXPATHLEN] = L'\0';
-            err = joinpath(exec_prefix, pybuilddir);
+            if (safe_wcscpy(exec_prefix, calculate->argv0_path, exec_prefix_len) < 0) {
+                return PATHLEN_ERR();
+            }
+            err = joinpath(exec_prefix, pybuilddir, exec_prefix_len);
             PyMem_RawFree(pybuilddir );
             if (_Py_INIT_FAILED(err)) {
                 return err;
@@ -588,18 +620,18 @@ search_for_exec_prefix(const _PyCoreConfig *core_config,
     }
 
     /* Search from argv0_path, until root is found */
-    err = copy_absolute(exec_prefix, calculate->argv0_path, MAXPATHLEN+1);
+    err = copy_absolute(exec_prefix, calculate->argv0_path, exec_prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
 
     do {
         n = wcslen(exec_prefix);
-        err = joinpath(exec_prefix, calculate->lib_python);
+        err = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
-        err = joinpath(exec_prefix, L"lib-dynload");
+        err = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -612,13 +644,14 @@ search_for_exec_prefix(const _PyCoreConfig *core_config,
     } while (exec_prefix[0]);
 
     /* Look at configure's EXEC_PREFIX */
-    wcsncpy(exec_prefix, calculate->exec_prefix, MAXPATHLEN);
-    exec_prefix[MAXPATHLEN] = L'\0';
-    err = joinpath(exec_prefix, calculate->lib_python);
+    if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) {
+        return PATHLEN_ERR();
+    }
+    err = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
-    err = joinpath(exec_prefix, L"lib-dynload");
+    err = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -635,11 +668,13 @@ search_for_exec_prefix(const _PyCoreConfig *core_config,
 
 static _PyInitError
 calculate_exec_prefix(const _PyCoreConfig *core_config,
-                      PyCalculatePath *calculate, wchar_t *exec_prefix)
+                      PyCalculatePath *calculate,
+                      wchar_t *exec_prefix, size_t exec_prefix_len)
 {
     _PyInitError err;
 
-    err = search_for_exec_prefix(core_config, calculate, exec_prefix,
+    err = search_for_exec_prefix(core_config, calculate,
+                                 exec_prefix, exec_prefix_len,
                                  &calculate->exec_prefix_found);
     if (_Py_INIT_FAILED(err)) {
         return err;
@@ -650,8 +685,10 @@ calculate_exec_prefix(const _PyCoreConfig *core_config,
             fprintf(stderr,
                 "Could not find platform dependent libraries <exec_prefix>\n");
         }
-        wcsncpy(exec_prefix, calculate->exec_prefix, MAXPATHLEN);
-        err = joinpath(exec_prefix, L"lib/lib-dynload");
+        if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) {
+            return PATHLEN_ERR();
+        }
+        err = joinpath(exec_prefix, L"lib/lib-dynload", exec_prefix_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -661,8 +698,9 @@ calculate_exec_prefix(const _PyCoreConfig *core_config,
 }
 
 
-static void
-calculate_reduce_exec_prefix(PyCalculatePath *calculate, wchar_t *exec_prefix)
+static _PyInitError
+calculate_reduce_exec_prefix(PyCalculatePath *calculate,
+                             wchar_t *exec_prefix, size_t exec_prefix_len)
 {
     if (calculate->exec_prefix_found > 0) {
         reduce(exec_prefix);
@@ -673,8 +711,11 @@ calculate_reduce_exec_prefix(PyCalculatePath *calculate, wchar_t *exec_prefix)
         }
     }
     else {
-        wcsncpy(exec_prefix, calculate->exec_prefix, MAXPATHLEN);
+        if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) {
+            return PATHLEN_ERR();
+        }
     }
+    return _Py_INIT_OK();
 }
 
 
@@ -683,16 +724,17 @@ calculate_program_full_path(const _PyCoreConfig *core_config,
                             PyCalculatePath *calculate, _PyPathConfig *config)
 {
     _PyInitError err;
-    wchar_t program_full_path[MAXPATHLEN+1];
+    wchar_t program_full_path[MAXPATHLEN + 1];
+    const size_t program_full_path_len = Py_ARRAY_LENGTH(program_full_path);
     memset(program_full_path, 0, sizeof(program_full_path));
 
 #ifdef __APPLE__
+    char execpath[MAXPATHLEN + 1];
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
-    uint32_t nsexeclength = MAXPATHLEN;
+    uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
 #else
-    unsigned long nsexeclength = MAXPATHLEN;
+    unsigned long nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
 #endif
-    char execpath[MAXPATHLEN+1];
 #endif
 
     /* If there is no slash in the argv0 path, then we have to
@@ -701,7 +743,10 @@ calculate_program_full_path(const _PyCoreConfig *core_config,
      * $PATH isn't exported, you lose.
      */
     if (wcschr(core_config->program_name, SEP)) {
-        wcsncpy(program_full_path, core_config->program_name, MAXPATHLEN);
+        if (safe_wcscpy(program_full_path, core_config->program_name,
+                        program_full_path_len) < 0) {
+            return PATHLEN_ERR();
+        }
     }
 #ifdef __APPLE__
      /* On Mac OS X, if a script uses an interpreter of the form
@@ -722,7 +767,10 @@ calculate_program_full_path(const _PyCoreConfig *core_config,
         if (path == NULL) {
             return DECODE_LOCALE_ERR("executable path", len);
         }
-        wcsncpy(program_full_path, path, MAXPATHLEN);
+        if (safe_wcscpy(program_full_path, path, program_full_path_len) < 0) {
+            PyMem_RawFree(path);
+            return PATHLEN_ERR();
+        }
         PyMem_RawFree(path);
     }
 #endif /* __APPLE__ */
@@ -733,17 +781,21 @@ calculate_program_full_path(const _PyCoreConfig *core_config,
 
             if (delim) {
                 size_t len = delim - path;
-                if (len > MAXPATHLEN) {
-                    len = MAXPATHLEN;
+                if (len >= program_full_path_len) {
+                    return PATHLEN_ERR();
                 }
                 wcsncpy(program_full_path, path, len);
                 program_full_path[len] = '\0';
             }
             else {
-                wcsncpy(program_full_path, path, MAXPATHLEN);
+                if (safe_wcscpy(program_full_path, path,
+                                program_full_path_len) < 0) {
+                    return PATHLEN_ERR();
+                }
             }
 
-            err = joinpath(program_full_path, core_config->program_name);
+            err = joinpath(program_full_path, core_config->program_name,
+                            program_full_path_len);
             if (_Py_INIT_FAILED(err)) {
                 return err;
             }
@@ -763,7 +815,7 @@ calculate_program_full_path(const _PyCoreConfig *core_config,
         program_full_path[0] = '\0';
     }
     if (program_full_path[0] != SEP && program_full_path[0] != '\0') {
-        err = absolutize(program_full_path);
+        err = absolutize(program_full_path, program_full_path_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -775,7 +827,7 @@ calculate_program_full_path(const _PyCoreConfig *core_config,
      * path (bpo-28441).
      */
     if (program_full_path[0] != '\0') {
-        err = add_exe_suffix(program_full_path);
+        err = add_exe_suffix(program_full_path, program_full_path_len);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -793,8 +845,10 @@ calculate_program_full_path(const _PyCoreConfig *core_config,
 static _PyInitError
 calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path)
 {
-    wcsncpy(calculate->argv0_path, program_full_path, MAXPATHLEN);
-    calculate->argv0_path[MAXPATHLEN] = '\0';
+    const size_t argv0_path_len = Py_ARRAY_LENGTH(calculate->argv0_path);
+    if (safe_wcscpy(calculate->argv0_path, program_full_path, argv0_path_len) < 0) {
+        return PATHLEN_ERR();
+    }
 
 #ifdef WITH_NEXT_FRAMEWORK
     NSModule pythonModule;
@@ -823,50 +877,61 @@ calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_pat
             return DECODE_LOCALE_ERR("framework location", len);
         }
 
-        wcsncpy(calculate->argv0_path, wbuf, MAXPATHLEN);
+        if (safe_wcscpy(calculate->argv0_path, wbuf, argv0_path_len) < 0) {
+            return PATHLEN_ERR();
+        }
         reduce(calculate->argv0_path);
-        err = joinpath(calculate->argv0_path, calculate->lib_python);
+        err = joinpath(calculate->argv0_path, calculate->lib_python, argv0_path_len);
         if (_Py_INIT_FAILED(err)) {
             PyMem_RawFree(wbuf);
             return err;
         }
-        err = joinpath(calculate->argv0_path, LANDMARK);
+        err = joinpath(calculate->argv0_path, LANDMARK, argv0_path_len);
         if (_Py_INIT_FAILED(err)) {
             PyMem_RawFree(wbuf);
             return err;
         }
-        if (!ismodule(calculate->argv0_path)) {
+        if (!ismodule(calculate->argv0_path,
+                      Py_ARRAY_LENGTH(calculate->argv0_path))) {
             /* We are in the build directory so use the name of the
                executable - we know that the absolute path is passed */
-            wcsncpy(calculate->argv0_path, program_full_path, MAXPATHLEN);
+            if (safe_wcscpy(calculate->argv0_path, program_full_path,
+                            argv0_path_len) < 0) {
+                return PATHLEN_ERR();
+            }
         }
         else {
             /* Use the location of the library as the program_full_path */
-            wcsncpy(calculate->argv0_path, wbuf, MAXPATHLEN);
+            if (safe_wcscpy(calculate->argv0_path, wbuf, argv0_path_len) < 0) {
+                return PATHLEN_ERR();
+            }
         }
         PyMem_RawFree(wbuf);
     }
 #endif
 
 #if HAVE_READLINK
-    wchar_t tmpbuffer[MAXPATHLEN+1];
-    int linklen = _Py_wreadlink(program_full_path, tmpbuffer, MAXPATHLEN);
+    wchar_t tmpbuffer[MAXPATHLEN + 1];
+    const size_t buflen = Py_ARRAY_LENGTH(tmpbuffer);
+    int linklen = _Py_wreadlink(program_full_path, tmpbuffer, buflen);
     while (linklen != -1) {
         if (tmpbuffer[0] == SEP) {
             /* tmpbuffer should never be longer than MAXPATHLEN,
                but extra check does not hurt */
-            wcsncpy(calculate->argv0_path, tmpbuffer, MAXPATHLEN);
+            if (safe_wcscpy(calculate->argv0_path, tmpbuffer, argv0_path_len) < 0) {
+                return PATHLEN_ERR();
+            }
         }
         else {
             /* Interpret relative to program_full_path */
             _PyInitError err;
             reduce(calculate->argv0_path);
-            err = joinpath(calculate->argv0_path, tmpbuffer);
+            err = joinpath(calculate->argv0_path, tmpbuffer, argv0_path_len);
             if (_Py_INIT_FAILED(err)) {
                 return err;
             }
         }
-        linklen = _Py_wreadlink(calculate->argv0_path, tmpbuffer, MAXPATHLEN);
+        linklen = _Py_wreadlink(calculate->argv0_path, tmpbuffer, buflen);
     }
 #endif /* HAVE_READLINK */
 
@@ -886,12 +951,15 @@ calculate_read_pyenv(PyCalculatePath *calculate)
 {
     _PyInitError err;
     wchar_t tmpbuffer[MAXPATHLEN+1];
+    const size_t buflen = Py_ARRAY_LENGTH(tmpbuffer);
     wchar_t *env_cfg = L"pyvenv.cfg";
     FILE *env_file;
 
-    wcscpy(tmpbuffer, calculate->argv0_path);
+    if (safe_wcscpy(tmpbuffer, calculate->argv0_path, buflen) < 0) {
+        return PATHLEN_ERR();
+    }
 
-    err = joinpath(tmpbuffer, env_cfg);
+    err = joinpath(tmpbuffer, env_cfg, buflen);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -901,7 +969,7 @@ calculate_read_pyenv(PyCalculatePath *calculate)
 
         reduce(tmpbuffer);
         reduce(tmpbuffer);
-        err = joinpath(tmpbuffer, env_cfg);
+        err = joinpath(tmpbuffer, env_cfg, buflen);
         if (_Py_INIT_FAILED(err)) {
             return err;
         }
@@ -917,8 +985,11 @@ calculate_read_pyenv(PyCalculatePath *calculate)
     }
 
     /* Look for a 'home' variable and set argv0_path to it, if found */
-    if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, MAXPATHLEN)) {
-        wcscpy(calculate->argv0_path, tmpbuffer);
+    if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, buflen)) {
+        if (safe_wcscpy(calculate->argv0_path, tmpbuffer,
+                        Py_ARRAY_LENGTH(calculate->argv0_path)) < 0) {
+            return PATHLEN_ERR();
+        }
     }
     fclose(env_file);
     return _Py_INIT_OK();
@@ -929,8 +1000,10 @@ static _PyInitError
 calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix)
 {
     _PyInitError err;
-    wcsncpy(calculate->zip_path, prefix, MAXPATHLEN);
-    calculate->zip_path[MAXPATHLEN] = L'\0';
+    const size_t zip_path_len = Py_ARRAY_LENGTH(calculate->zip_path);
+    if (safe_wcscpy(calculate->zip_path, prefix, zip_path_len) < 0) {
+        return PATHLEN_ERR();
+    }
 
     if (calculate->prefix_found > 0) {
         /* Use the reduced prefix returned by Py_GetPrefix() */
@@ -938,9 +1011,11 @@ calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix)
         reduce(calculate->zip_path);
     }
     else {
-        wcsncpy(calculate->zip_path, calculate->prefix, MAXPATHLEN);
+        if (safe_wcscpy(calculate->zip_path, calculate->prefix, zip_path_len) < 0) {
+            return PATHLEN_ERR();
+        }
     }
-    err = joinpath(calculate->zip_path, L"lib/python00.zip");
+    err = joinpath(calculate->zip_path, L"lib/python00.zip", zip_path_len);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -1111,7 +1186,8 @@ calculate_path_impl(const _PyCoreConfig *core_config,
 
     wchar_t prefix[MAXPATHLEN+1];
     memset(prefix, 0, sizeof(prefix));
-    err = calculate_prefix(core_config, calculate, prefix);
+    err = calculate_prefix(core_config, calculate,
+                           prefix, Py_ARRAY_LENGTH(prefix));
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -1123,7 +1199,8 @@ calculate_path_impl(const _PyCoreConfig *core_config,
 
     wchar_t exec_prefix[MAXPATHLEN+1];
     memset(exec_prefix, 0, sizeof(exec_prefix));
-    err = calculate_exec_prefix(core_config, calculate, exec_prefix);
+    err = calculate_exec_prefix(core_config, calculate,
+                                exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -1141,14 +1218,21 @@ calculate_path_impl(const _PyCoreConfig *core_config,
         return err;
     }
 
-    calculate_reduce_prefix(calculate, prefix);
+    err = calculate_reduce_prefix(calculate, prefix, Py_ARRAY_LENGTH(prefix));
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
 
     config->prefix = _PyMem_RawWcsdup(prefix);
     if (config->prefix == NULL) {
         return _Py_INIT_NO_MEMORY();
     }
 
-    calculate_reduce_exec_prefix(calculate, exec_prefix);
+    err = calculate_reduce_exec_prefix(calculate,
+                                       exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
 
     config->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
     if (config->exec_prefix == NULL) {
diff --git a/Python/fileutils.c b/Python/fileutils.c
index 0ac690a211cf..b933874193b4 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -1716,7 +1716,7 @@ _Py_wrealpath(const wchar_t *path,
 }
 #endif
 
-/* Get the current directory. size is the buffer size in wide characters
+/* Get the current directory. buflen is the buffer size in wide characters
    including the null character. Decode the path from the locale encoding.
 
    Return NULL on getcwd() error, on decoding error, or if 'buf' is
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index 87db66b7528c..f1818eb30765 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -578,8 +578,8 @@ _PyPathConfig_ComputeArgv0(const _PyWstrList *argv)
     int have_script_arg = 0;
     int have_module_arg = 0;
 #ifdef HAVE_READLINK
-    wchar_t link[MAXPATHLEN+1];
-    wchar_t argv0copy[2*MAXPATHLEN+1];
+    wchar_t link[MAXPATHLEN + 1];
+    wchar_t argv0copy[2 * MAXPATHLEN + 1];
     int nr = 0;
 #endif
 #if defined(HAVE_REALPATH)
@@ -607,7 +607,7 @@ _PyPathConfig_ComputeArgv0(const _PyWstrList *argv)
 
 #ifdef HAVE_READLINK
     if (have_script_arg)
-        nr = _Py_wreadlink(argv0, link, MAXPATHLEN);
+        nr = _Py_wreadlink(argv0, link, Py_ARRAY_LENGTH(link));
     if (nr > 0) {
         /* It's a symlink */
         link[nr] = '\0';
@@ -692,11 +692,12 @@ _Py_FindEnvConfigValue(FILE *env_file, const wchar_t *key,
                        wchar_t *value, size_t value_size)
 {
     int result = 0; /* meaning not found */
-    char buffer[MAXPATHLEN*2+1];  /* allow extra for key, '=', etc. */
+    char buffer[MAXPATHLEN * 2 + 1];  /* allow extra for key, '=', etc. */
+    buffer[Py_ARRAY_LENGTH(buffer)-1] = '\0';
 
     fseek(env_file, 0, SEEK_SET);
     while (!feof(env_file)) {
-        char * p = fgets(buffer, MAXPATHLEN*2, env_file);
+        char * p = fgets(buffer, Py_ARRAY_LENGTH(buffer) - 1, env_file);
 
         if (p == NULL) {
             break;
@@ -721,7 +722,8 @@ _Py_FindEnvConfigValue(FILE *env_file, const wchar_t *key,
                 if ((tok != NULL) && !wcscmp(tok, L"=")) {
                     tok = WCSTOK(NULL, L"\r\n", &state);
                     if (tok != NULL) {
-                        wcsncpy(value, tok, MAXPATHLEN);
+                        wcsncpy(value, tok, value_size - 1);
+                        value[value_size - 1] = L'\0';
                         result = 1;
                         PyMem_RawFree(tmpbuffer);
                         break;



More information about the Python-checkins mailing list