summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@microsoft.com>2015-05-22 22:10:10 (GMT)
committerSteve Dower <steve.dower@microsoft.com>2015-05-22 22:10:10 (GMT)
commit4a7fe7e3975062a939a4e7242f2a12b172befd8a (patch)
tree7d7eeee5aa9b075cd6838691e2f25b6c3a795b9c
parentd9ef74e3ddffc9327a3b225734cdc5a5028e8d0b (diff)
downloadcpython-4a7fe7e3975062a939a4e7242f2a12b172befd8a.zip
cpython-4a7fe7e3975062a939a4e7242f2a12b172befd8a.tar.gz
cpython-4a7fe7e3975062a939a4e7242f2a12b172befd8a.tar.bz2
Issue #23955: Add pyvenv.cfg option to suppress registry/environment lookup for generating sys.path.
Also cleans up and secures getpathp.c
-rw-r--r--Doc/using/windows.rst21
-rw-r--r--Misc/NEWS3
-rw-r--r--PC/getpathp.c178
-rw-r--r--Tools/msi/make_zip.py3
4 files changed, 127 insertions, 78 deletions
diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst
index 999ec1f..8e3c110 100644
--- a/Doc/using/windows.rst
+++ b/Doc/using/windows.rst
@@ -661,6 +661,17 @@ This is how :data:`sys.path` is populated on Windows:
the environment, and no registry entries can be found, a default path with
relative entries is used (e.g. ``.\Lib;.\plat-win``, etc).
+If a ``pyvenv.cfg`` file is found alongside the main executable or in the
+directory one level above the executable, the following variations apply:
+
+* If ``home`` is an absolute path and :envvar:`PYTHONHOME` is not set, this
+ path is used instead of the path to the main executable when deducing the
+ home location.
+
+* If ``applocal`` is set to true, the ``home`` property or the main executable
+ is always used as the home path, and all environment variables or registry
+ values affecting the path are ignored. The landmark file is not checked.
+
The end result of all this is:
* When running :file:`python.exe`, or any other .exe in the main Python
@@ -672,13 +683,17 @@ The end result of all this is:
etc), the "Python Home" will not be deduced, so the core path from the
registry is used. Other "application paths" in the registry are always read.
-* If Python can't find its home and there is no registry (eg, frozen .exe, some
- very strange installation setup) you get a path with some default, but
+* If Python can't find its home and there are no registry value (frozen .exe,
+ some very strange installation setup) you get a path with some default, but
relative, paths.
For those who want to bundle Python into their application or distribution, the
following advice will prevent conflicts with other installations:
+* Include a ``pyvenv.cfg`` file alongside your executable containing
+ ``applocal = true``. This will ensure that your own directory will be used to
+ resolve paths even if you have included the standard library in a ZIP file.
+
* If you are loading :file:`python3.dll` or :file:`python35.dll` in your own
executable, explicitly call :c:func:`Py_SetPath` or (at least)
:c:func:`Py_SetProgramName` before :c:func:`Py_Initialize`.
@@ -688,7 +703,7 @@ following advice will prevent conflicts with other installations:
* If you cannot use the previous suggestions (for example, you are a
distribution that allows people to run :file:`python.exe` directly), ensure
- that the landmark file (:file:`Lib\\os.py`) exists in your bundled library.
+ that the landmark file (:file:`Lib\\os.py`) exists in your install directory.
(Note that it will not be detected inside a ZIP file.)
These will ensure that the files in a system-wide installation will not take
diff --git a/Misc/NEWS b/Misc/NEWS
index a62cca1..3a282d0 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ Release date: 2015-05-24
Core and Builtins
-----------------
+- Issue #23955: Add pyvenv.cfg option to suppress registry/environment
+ lookup for generating sys.path on Windows.
+
- Issue #24257: Fixed system error in the comparison of faked
types.SimpleNamespace.
diff --git a/PC/getpathp.c b/PC/getpathp.c
index d4f4574..08b65c5 100644
--- a/PC/getpathp.c
+++ b/PC/getpathp.c
@@ -113,7 +113,10 @@ is_sep(wchar_t ch) /* determine if "ch" is a separator character */
static void
reduce(wchar_t *dir)
{
- size_t i = wcslen(dir);
+ size_t i = wcsnlen_s(dir, MAXPATHLEN+1);
+ if (i >= MAXPATHLEN+1)
+ Py_FatalError("buffer overflow in getpathp.c's reduce()");
+
while (i > 0 && !is_sep(dir[i]))
--i;
dir[i] = '\0';
@@ -130,16 +133,23 @@ exists(wchar_t *filename)
may extend 'filename' by one character.
*/
static int
-ismodule(wchar_t *filename) /* Is module -- check for .pyc/.pyo too */
+ismodule(wchar_t *filename, int update_filename) /* Is module -- check for .pyc/.pyo too */
{
+ int n;
+
if (exists(filename))
return 1;
/* Check for the compiled version of prefix. */
- if (wcslen(filename) < MAXPATHLEN) {
- wcscat(filename, Py_OptimizeFlag ? L"o" : L"c");
- if (exists(filename))
- return 1;
+ n = wcsnlen_s(filename, MAXPATHLEN+1);
+ if (n < MAXPATHLEN) {
+ int exist = 0;
+ filename[n] = Py_OptimizeFlag ? L'o' : L'c';
+ filename[n + 1] = L'\0';
+ exist = exists(filename);
+ if (!update_filename)
+ filename[n] = L'\0';
+ return exist;
}
return 0;
}
@@ -154,23 +164,23 @@ ismodule(wchar_t *filename) /* Is module -- check for .pyc/.pyo too */
stuff as fits will be appended.
*/
static void
-join(wchar_t *buffer, wchar_t *stuff)
+join(wchar_t *buffer, const wchar_t *stuff)
{
- size_t n, k;
- if (is_sep(stuff[0]))
- n = 0;
- else {
- n = wcslen(buffer);
- if (n > 0 && !is_sep(buffer[n-1]) && n < MAXPATHLEN)
- buffer[n++] = SEP;
+ size_t n;
+ if (is_sep(stuff[0]) ||
+ (wcsnlen_s(stuff, 4) >= 3 && stuff[1] == ':' && is_sep(stuff[2]))) {
+ if (wcscpy_s(buffer, MAXPATHLEN+1, stuff) != 0)
+ Py_FatalError("buffer overflow in getpathp.c's join()");
+ return;
+ }
+
+ n = wcsnlen_s(buffer, MAXPATHLEN+1);
+ if (n > 0 && !is_sep(buffer[n - 1]) && n < MAXPATHLEN) {
+ buffer[n] = SEP;
+ buffer[n + 1] = '\0';
}
- if (n > MAXPATHLEN)
- Py_FatalError("buffer overflow in getpathp.c's joinpath()");
- k = wcslen(stuff);
- if (n + k > MAXPATHLEN)
- k = MAXPATHLEN - n;
- wcsncpy(buffer+n, stuff, k);
- buffer[n+k] = '\0';
+ if (wcscat_s(buffer, MAXPATHLEN+1, stuff) != 0)
+ Py_FatalError("buffer overflow in getpathp.c's join()");
}
/* gotlandmark only called by search_for_prefix, which ensures
@@ -181,11 +191,10 @@ static int
gotlandmark(wchar_t *landmark)
{
int ok;
- Py_ssize_t n;
+ Py_ssize_t n = wcsnlen_s(prefix, MAXPATHLEN);
- n = wcslen(prefix);
join(prefix, landmark);
- ok = ismodule(prefix);
+ ok = ismodule(prefix, FALSE);
prefix[n] = '\0';
return ok;
}
@@ -196,7 +205,7 @@ static int
search_for_prefix(wchar_t *argv0_path, wchar_t *landmark)
{
/* Search from argv0_path, until landmark is found */
- wcscpy(prefix, argv0_path);
+ wcscpy_s(prefix, MAXPATHLEN + 1, argv0_path);
do {
if (gotlandmark(landmark))
return 1;
@@ -236,7 +245,7 @@ getpythonregpath(HKEY keyBase, int skipcore)
WCHAR *dataBuf = NULL;
static const WCHAR keyPrefix[] = L"Software\\Python\\PythonCore\\";
static const WCHAR keySuffix[] = L"\\PythonPath";
- size_t versionLen;
+ size_t versionLen, keyBufLen;
DWORD index;
WCHAR *keyBuf = NULL;
WCHAR *keyBufPtr;
@@ -245,12 +254,13 @@ getpythonregpath(HKEY keyBase, int skipcore)
/* Tried to use sysget("winver") but here is too early :-( */
versionLen = strlen(PyWin_DLLVersionString);
/* Space for all the chars, plus one \0 */
- keyBuf = keyBufPtr = PyMem_RawMalloc(sizeof(keyPrefix) +
- sizeof(WCHAR)*(versionLen-1) +
- sizeof(keySuffix));
+ keyBufLen = sizeof(keyPrefix) +
+ sizeof(WCHAR)*(versionLen-1) +
+ sizeof(keySuffix);
+ keyBuf = keyBufPtr = PyMem_RawMalloc(keyBufLen);
if (keyBuf==NULL) goto done;
- memcpy(keyBufPtr, keyPrefix, sizeof(keyPrefix)-sizeof(WCHAR));
+ memcpy_s(keyBufPtr, keyBufLen, keyPrefix, sizeof(keyPrefix)-sizeof(WCHAR));
keyBufPtr += Py_ARRAY_LENGTH(keyPrefix) - 1;
mbstowcs(keyBufPtr, PyWin_DLLVersionString, versionLen);
keyBufPtr += versionLen;
@@ -484,7 +494,7 @@ calculate_path(void)
wchar_t *machinepath = NULL;
wchar_t *userpath = NULL;
wchar_t zip_path[MAXPATHLEN+1];
- size_t len;
+ int applocal = 0;
if (!Py_IgnoreEnvironmentFlag) {
envpath = _wgetenv(L"PYTHONPATH");
@@ -502,7 +512,7 @@ calculate_path(void)
get_progpath();
/* progpath guaranteed \0 terminated in MAXPATH+1 bytes. */
- wcscpy(argv0_path, progpath);
+ wcscpy_s(argv0_path, MAXPATHLEN+1, progpath);
reduce(argv0_path);
/* Search for an environment configuration file, first in the
@@ -511,27 +521,39 @@ calculate_path(void)
*/
{
+ wchar_t envbuffer[MAXPATHLEN+1];
wchar_t tmpbuffer[MAXPATHLEN+1];
- wchar_t *env_cfg = L"pyvenv.cfg";
+ const wchar_t *env_cfg = L"pyvenv.cfg";
FILE * env_file = NULL;
- wcscpy(tmpbuffer, argv0_path);
- join(tmpbuffer, env_cfg);
- env_file = _Py_wfopen(tmpbuffer, L"r");
+ wcscpy_s(envbuffer, MAXPATHLEN+1, argv0_path);
+ join(envbuffer, env_cfg);
+ env_file = _Py_wfopen(envbuffer, L"r");
if (env_file == NULL) {
errno = 0;
- reduce(tmpbuffer);
- reduce(tmpbuffer);
- join(tmpbuffer, env_cfg);
- env_file = _Py_wfopen(tmpbuffer, L"r");
+ reduce(envbuffer);
+ reduce(envbuffer);
+ join(envbuffer, env_cfg);
+ env_file = _Py_wfopen(envbuffer, L"r");
if (env_file == NULL) {
errno = 0;
}
}
if (env_file != NULL) {
+ /* Look for an 'applocal' variable and, if true, ignore all registry
+ * keys and environment variables, but retain the default paths
+ * (DLLs, Lib) and the zip file. Setting pythonhome here suppresses
+ * the search for LANDMARK below and overrides %PYTHONHOME%.
+ */
+ if (find_env_config_value(env_file, L"applocal", tmpbuffer) &&
+ (applocal = (wcsicmp(tmpbuffer, L"true") == 0))) {
+ envpath = NULL;
+ pythonhome = argv0_path;
+ }
+
/* Look for a 'home' variable and set argv0_path to it, if found */
if (find_env_config_value(env_file, L"home", tmpbuffer)) {
- wcscpy(argv0_path, tmpbuffer);
+ wcscpy_s(argv0_path, MAXPATHLEN+1, tmpbuffer);
}
fclose(env_file);
env_file = NULL;
@@ -545,33 +567,30 @@ calculate_path(void)
pythonhome = NULL;
}
else
- wcsncpy(prefix, pythonhome, MAXPATHLEN);
+ wcscpy_s(prefix, MAXPATHLEN+1, pythonhome);
if (envpath && *envpath == '\0')
envpath = NULL;
#ifdef MS_WINDOWS
- /* Calculate zip archive path */
- if (dllpath[0]) /* use name of python DLL */
- wcsncpy(zip_path, dllpath, MAXPATHLEN);
- else /* use name of executable program */
- wcsncpy(zip_path, progpath, MAXPATHLEN);
- zip_path[MAXPATHLEN] = '\0';
- len = wcslen(zip_path);
- if (len > 4) {
- zip_path[len-3] = 'z'; /* change ending to "zip" */
- zip_path[len-2] = 'i';
- zip_path[len-1] = 'p';
- }
+ /* Calculate zip archive path from DLL or exe path */
+ if (wcscpy_s(zip_path, MAXPATHLEN+1, dllpath[0] ? dllpath : progpath))
+ /* exceeded buffer length - ignore zip_path */
+ zip_path[0] = '\0';
else {
- zip_path[0] = 0;
+ wchar_t *dot = wcsrchr(zip_path, '.');
+ if (!dot || wcscpy_s(dot, MAXPATHLEN+1 - (dot - zip_path), L".zip"))
+ /* exceeded buffer length - ignore zip_path */
+ zip_path[0] = L'\0';
}
skiphome = pythonhome==NULL ? 0 : 1;
#ifdef Py_ENABLE_SHARED
- machinepath = getpythonregpath(HKEY_LOCAL_MACHINE, skiphome);
- userpath = getpythonregpath(HKEY_CURRENT_USER, skiphome);
+ if (!applocal) {
+ machinepath = getpythonregpath(HKEY_LOCAL_MACHINE, skiphome);
+ userpath = getpythonregpath(HKEY_CURRENT_USER, skiphome);
+ }
#endif
/* We only use the default relative PYTHONPATH if we havent
anything better to use! */
@@ -590,6 +609,7 @@ calculate_path(void)
Extra rules:
- If PYTHONHOME is set (in any way) item (3) is ignored.
- If registry values are used, (4) and (5) are ignored.
+ - If applocal is set, (1), (3), and registry values are ignored
*/
/* Calculate size of return buffer */
@@ -600,21 +620,21 @@ calculate_path(void)
if (*p == DELIM)
bufsz++; /* number of DELIM plus one */
}
- bufsz *= wcslen(pythonhome);
+ bufsz *= wcsnlen_s(pythonhome, MAXPATHLEN+1);
}
else
bufsz = 0;
- bufsz += wcslen(PYTHONPATH) + 1;
- bufsz += wcslen(argv0_path) + 1;
+ bufsz += wcsnlen_s(PYTHONPATH, MAXPATHLEN+1) + 1;
+ bufsz += wcsnlen_s(argv0_path, MAXPATHLEN+1) + 1;
#ifdef MS_WINDOWS
- if (userpath)
- bufsz += wcslen(userpath) + 1;
- if (machinepath)
- bufsz += wcslen(machinepath) + 1;
- bufsz += wcslen(zip_path) + 1;
+ if (!applocal && userpath)
+ bufsz += wcsnlen_s(userpath, MAXPATHLEN+1) + 1;
+ if (!applocal && machinepath)
+ bufsz += wcsnlen_s(machinepath, MAXPATHLEN+1) + 1;
+ bufsz += wcsnlen_s(zip_path, MAXPATHLEN+1) + 1;
#endif
if (envpath != NULL)
- bufsz += wcslen(envpath) + 1;
+ bufsz += wcsnlen_s(envpath, MAXPATHLEN+1) + 1;
module_search_path = buf = PyMem_RawMalloc(bufsz*sizeof(wchar_t));
if (buf == NULL) {
@@ -636,38 +656,45 @@ calculate_path(void)
}
if (envpath) {
- wcscpy(buf, envpath);
+ if (wcscpy_s(buf, bufsz - (buf - module_search_path), envpath))
+ Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
buf = wcschr(buf, L'\0');
*buf++ = DELIM;
}
#ifdef MS_WINDOWS
if (zip_path[0]) {
- wcscpy(buf, zip_path);
+ if (wcscpy_s(buf, bufsz - (buf - module_search_path), zip_path))
+ Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
buf = wcschr(buf, L'\0');
*buf++ = DELIM;
}
if (userpath) {
- wcscpy(buf, userpath);
+ if (wcscpy_s(buf, bufsz - (buf - module_search_path), userpath))
+ Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
buf = wcschr(buf, L'\0');
*buf++ = DELIM;
PyMem_RawFree(userpath);
}
if (machinepath) {
- wcscpy(buf, machinepath);
+ if (wcscpy_s(buf, bufsz - (buf - module_search_path), machinepath))
+ Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
buf = wcschr(buf, L'\0');
*buf++ = DELIM;
PyMem_RawFree(machinepath);
}
if (pythonhome == NULL) {
if (!skipdefault) {
- wcscpy(buf, PYTHONPATH);
+ if (wcscpy_s(buf, bufsz - (buf - module_search_path), PYTHONPATH))
+ Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
buf = wcschr(buf, L'\0');
+ *buf++ = DELIM;
}
}
#else
if (pythonhome == NULL) {
wcscpy(buf, PYTHONPATH);
buf = wcschr(buf, L'\0');
+ *buf++ = DELIM;
}
#endif /* MS_WINDOWS */
else {
@@ -681,25 +708,26 @@ calculate_path(void)
else
n = q-p;
if (p[0] == '.' && is_sep(p[1])) {
- wcscpy(buf, pythonhome);
+ if (wcscpy_s(buf, bufsz - (buf - module_search_path), pythonhome))
+ Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
buf = wcschr(buf, L'\0');
p++;
n--;
}
wcsncpy(buf, p, n);
buf += n;
+ *buf++ = DELIM;
if (q == NULL)
break;
- *buf++ = DELIM;
p = q+1;
}
}
if (argv0_path) {
- *buf++ = DELIM;
wcscpy(buf, argv0_path);
buf = wcschr(buf, L'\0');
+ *buf++ = DELIM;
}
- *buf = L'\0';
+ *(buf - 1) = L'\0';
/* Now to pull one last hack/trick. If sys.prefix is
empty, then try and find it somewhere on the paths
we calculated. We scan backwards, as our general policy
diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py
index 72ec280..fcfcd56 100644
--- a/Tools/msi/make_zip.py
+++ b/Tools/msi/make_zip.py
@@ -149,6 +149,9 @@ def main():
copied = copy_to_layout(temp / t.rstrip('/'), rglob(s, p, c))
print('Copied {} files'.format(copied))
+ with open(str(temp / 'pyvenv.cfg'), 'w') as f:
+ print('applocal = true', file=f)
+
total = copy_to_layout(out, rglob(temp, '*', None))
print('Wrote {} files to {}'.format(total, out))
finally: