Issue #15905: Fix theoretical buffer overflow in handling of sys.argv[0],
prefix and exec_prefix if the operation system does not obey MAXPATHLEN.
diff --git a/Misc/NEWS b/Misc/NEWS
index 3092e32..2b28991 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@
 Core and Builtins
 -----------------
 
+- Issue #15905: Fix theoretical buffer overflow in handling of sys.argv[0],
+  prefix and exec_prefix if the operation system does not obey MAXPATHLEN.
+
 - Issue #18344: Fix potential ref-leaks in _bufferedreader_read_all().
 
 - Issue #17872: Fix a segfault in marshal.load() when input stream returns
diff --git a/Modules/getpath.c b/Modules/getpath.c
index b98c520..ff14fdd 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -326,6 +326,7 @@
     if (home) {
         wchar_t *delim;
         wcsncpy(prefix, home, MAXPATHLEN);
+        prefix[MAXPATHLEN] = L'\0';
         delim = wcschr(prefix, DELIM);
         if (delim)
             *delim = L'\0';
@@ -335,13 +336,15 @@
     }
 
     /* Check to see if argv[0] is in the build directory */
-    wcscpy(prefix, argv0_path);
+    wcsncpy(prefix, argv0_path, MAXPATHLEN);
+    prefix[MAXPATHLEN] = L'\0';
     joinpath(prefix, L"Modules/Setup");
     if (isfile(prefix)) {
         /* Check VPATH to see if argv0_path is in the build directory. */
         vpath = _Py_char2wchar(VPATH, NULL);
         if (vpath != NULL) {
-            wcscpy(prefix, argv0_path);
+            wcsncpy(prefix, argv0_path, MAXPATHLEN);
+            prefix[MAXPATHLEN] = L'\0';
             joinpath(prefix, vpath);
             PyMem_Free(vpath);
             joinpath(prefix, L"Lib");
@@ -365,6 +368,7 @@
 
     /* Look at configure's PREFIX */
     wcsncpy(prefix, _prefix, MAXPATHLEN);
+    prefix[MAXPATHLEN] = L'\0';
     joinpath(prefix, lib_python);
     joinpath(prefix, LANDMARK);
     if (ismodule(prefix))
@@ -391,6 +395,7 @@
             wcsncpy(exec_prefix, delim+1, MAXPATHLEN);
         else
             wcsncpy(exec_prefix, home, MAXPATHLEN);
+        exec_prefix[MAXPATHLEN] = L'\0';
         joinpath(exec_prefix, lib_python);
         joinpath(exec_prefix, L"lib-dynload");
         return 1;
@@ -399,7 +404,8 @@
     /* 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. */
-    wcscpy(exec_prefix, argv0_path);
+    wcsncpy(exec_prefix, argv0_path, MAXPATHLEN);
+    exec_prefix[MAXPATHLEN] = L'\0';
     joinpath(exec_prefix, L"pybuilddir.txt");
     if (isfile(exec_prefix)) {
         FILE *f = _Py_wfopen(exec_prefix, L"rb");
@@ -420,7 +426,8 @@
                 Py_DECREF(decoded);
                 if (k >= 0) {
                     rel_builddir_path[k] = L'\0';
-                    wcscpy(exec_prefix, argv0_path);
+                    wcsncpy(exec_prefix, argv0_path, MAXPATHLEN);
+                    exec_prefix[MAXPATHLEN] = L'\0';
                     joinpath(exec_prefix, rel_builddir_path);
                     return -1;
                 }
@@ -442,6 +449,7 @@
 
     /* Look at configure's EXEC_PREFIX */
     wcsncpy(exec_prefix, _exec_prefix, MAXPATHLEN);
+    exec_prefix[MAXPATHLEN] = L'\0';
     joinpath(exec_prefix, lib_python);
     joinpath(exec_prefix, L"lib-dynload");
     if (isdir(exec_prefix))
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 20bfa55..edd6649 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1856,10 +1856,11 @@
             if (q == NULL)
                 argv0 = link; /* argv0 without path */
             else {
-                /* Must make a copy */
-                wcscpy(argv0copy, argv0);
+                /* Must make a copy, argv0copy has room for 2 * MAXPATHLEN */
+                wcsncpy(argv0copy, argv0, MAXPATHLEN);
                 q = wcsrchr(argv0copy, SEP);
-                wcscpy(q+1, link);
+                wcsncpy(q+1, link, MAXPATHLEN);
+                q[MAXPATHLEN + 1] = L'\0';
                 argv0 = argv0copy;
             }
         }