bpo-34170: Rework _PyCoreConfig_Read() to avoid side effect (GH-8353)

Rework _PyCoreConfig_Read() function which *reads* core configuration
to not *modify* the path configuration.

A new _PyCoreConfig_SetPathConfig() function now recreates the path
configuration from the core configuration. This function is now
called very late in _Py_InitializeCore(), just before calling
initimport().

Changes:

* Add _PyCoreConfig.dll_path
* Py_SetPath() now fails with a fatal python error on memory
   allocation failure.
* Rename _PyPathConfig_Calculate() to _PyPathConfig_Calculate_impl()
* Replace _PyPathConfig_Init() with _PyPathConfig_Calculate(): the
  function now requires a _PyPathConfig
* Add _PyPathConfig_SetGlobal() to set the _Py_path_config global
  variable.
* Add _PyCoreConfig_InitPathConfig(): compute the path configuration
* Add _PyCoreConfig_SetPathConfig(): set path configuration from core
  configuration
* Rename wstrlist_append() to _Py_wstrlist_append()
* _Py_wstrlist_append() now handles integer overflow.
This commit is contained in:
Victor Stinner 2018-07-21 02:06:16 +02:00 committed by GitHub
parent 94487d4570
commit b1147e43da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 358 additions and 146 deletions

View file

@ -13,7 +13,23 @@ extern "C" {
_PyPathConfig _Py_path_config = _PyPathConfig_INIT;
void
static int
copy_wstr(wchar_t **dst, const wchar_t *src)
{
if (src != NULL) {
*dst = _PyMem_RawWcsdup(src);
if (*dst == NULL) {
return -1;
}
}
else {
*dst = NULL;
}
return 0;
}
static void
_PyPathConfig_Clear(_PyPathConfig *config)
{
/* _PyMem_SetDefaultAllocator() is needed to get a known memory allocator,
@ -44,16 +60,11 @@ _PyPathConfig_Clear(_PyPathConfig *config)
}
/* Initialize paths for Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix()
and Py_GetProgramFullPath() */
_PyInitError
_PyPathConfig_Init(const _PyCoreConfig *core_config)
/* Calculate the path configuration: initialize path_config from core_config */
static _PyInitError
_PyPathConfig_Calculate(_PyPathConfig *path_config,
const _PyCoreConfig *core_config)
{
if (_Py_path_config.module_search_path) {
/* Already initialized */
return _Py_INIT_OK();
}
_PyInitError err;
_PyPathConfig new_config = _PyPathConfig_INIT;
@ -62,31 +73,67 @@ _PyPathConfig_Init(const _PyCoreConfig *core_config)
/* Calculate program_full_path, prefix, exec_prefix (Unix)
or dll_path (Windows), and module_search_path */
err = _PyPathConfig_Calculate(&new_config, core_config);
err = _PyPathConfig_Calculate_impl(&new_config, core_config);
if (_Py_INIT_FAILED(err)) {
_PyPathConfig_Clear(&new_config);
goto done;
goto err;
}
/* Copy home and program_name from core_config */
if (core_config->home != NULL) {
new_config.home = _PyMem_RawWcsdup(core_config->home);
if (new_config.home == NULL) {
err = _Py_INIT_NO_MEMORY();
goto done;
}
if (copy_wstr(&new_config.home, core_config->home) < 0) {
err = _Py_INIT_NO_MEMORY();
goto err;
}
else {
new_config.home = NULL;
if (copy_wstr(&new_config.program_name, core_config->program_name) < 0) {
err = _Py_INIT_NO_MEMORY();
goto err;
}
new_config.program_name = _PyMem_RawWcsdup(core_config->program_name);
if (new_config.program_name == NULL) {
err = _Py_INIT_NO_MEMORY();
goto done;
}
_PyPathConfig_Clear(path_config);
*path_config = new_config;
err = _Py_INIT_OK();
goto done;
err:
_PyPathConfig_Clear(&new_config);
done:
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
return err;
}
_PyInitError
_PyPathConfig_SetGlobal(const _PyPathConfig *config)
{
_PyInitError err;
_PyPathConfig new_config = _PyPathConfig_INIT;
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
#define COPY_ATTR(ATTR) \
do { \
if (copy_wstr(&new_config.ATTR, config->ATTR) < 0) { \
_PyPathConfig_Clear(&new_config); \
err = _Py_INIT_NO_MEMORY(); \
goto done; \
} \
} while (0)
COPY_ATTR(program_full_path);
COPY_ATTR(prefix);
#ifdef MS_WINDOWS
COPY_ATTR(dll_path);
#else
COPY_ATTR(exec_prefix);
#endif
COPY_ATTR(module_search_path);
COPY_ATTR(program_name);
COPY_ATTR(home);
_PyPathConfig_Clear(&_Py_path_config);
/* Steal new_config strings; don't clear new_config */
_Py_path_config = new_config;
err = _Py_INIT_OK();
@ -97,15 +144,228 @@ done:
}
void
_PyPathConfig_ClearGlobal(void)
{
_PyPathConfig_Clear(&_Py_path_config);
}
static wchar_t*
wstrlist_join(wchar_t sep, int count, wchar_t **list)
{
size_t len = 1; /* NUL terminator */
for (int i=0; i < count; i++) {
if (i != 0) {
len++;
}
len += wcslen(list[i]);
}
wchar_t *text = PyMem_RawMalloc(len * sizeof(wchar_t));
if (text == NULL) {
return NULL;
}
wchar_t *str = text;
for (int i=0; i < count; i++) {
wchar_t *path = list[i];
if (i != 0) {
*str++ = SEP;
}
len = wcslen(path);
memcpy(str, path, len * sizeof(wchar_t));
str += len;
}
*str = L'\0';
return text;
}
/* Set the global path configuration from core_config. */
_PyInitError
_PyCoreConfig_SetPathConfig(const _PyCoreConfig *core_config)
{
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
_PyInitError err;
_PyPathConfig path_config = _PyPathConfig_INIT;
path_config.module_search_path = wstrlist_join(DELIM,
core_config->nmodule_search_path,
core_config->module_search_paths);
if (path_config.module_search_path == NULL) {
goto no_memory;
}
if (copy_wstr(&path_config.program_full_path, core_config->executable) < 0) {
goto no_memory;
}
if (copy_wstr(&path_config.prefix, core_config->prefix) < 0) {
goto no_memory;
}
#ifdef MS_WINDOWS
if (copy_wstr(&path_config.dll_path, core_config->dll_path) < 0) {
goto no_memory;
}
#else
if (copy_wstr(&path_config.exec_prefix, core_config->exec_prefix) < 0) {
goto no_memory;
}
#endif
if (copy_wstr(&path_config.program_name, core_config->program_name) < 0) {
goto no_memory;
}
if (copy_wstr(&path_config.home, core_config->home) < 0) {
goto no_memory;
}
err = _PyPathConfig_SetGlobal(&path_config);
if (_Py_INIT_FAILED(err)) {
goto done;
}
err = _Py_INIT_OK();
goto done;
no_memory:
err = _Py_INIT_NO_MEMORY();
done:
_PyPathConfig_Clear(&path_config);
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
return err;
}
static _PyInitError
core_config_init_module_search_paths(_PyCoreConfig *config,
_PyPathConfig *path_config)
{
assert(config->module_search_paths == NULL);
assert(config->nmodule_search_path < 0);
config->nmodule_search_path = 0;
const wchar_t *sys_path = path_config->module_search_path;
const wchar_t delim = DELIM;
const wchar_t *p = sys_path;
while (1) {
p = wcschr(sys_path, delim);
if (p == NULL) {
p = sys_path + wcslen(sys_path); /* End of string */
}
size_t path_len = (p - sys_path);
wchar_t *path = PyMem_RawMalloc((path_len + 1) * sizeof(wchar_t));
if (path == NULL) {
return _Py_INIT_NO_MEMORY();
}
memcpy(path, sys_path, path_len * sizeof(wchar_t));
path[path_len] = L'\0';
_PyInitError err = _Py_wstrlist_append(&config->nmodule_search_path,
&config->module_search_paths,
path);
PyMem_RawFree(path);
if (_Py_INIT_FAILED(err)) {
return err;
}
if (*p == '\0') {
break;
}
sys_path = p + 1;
}
return _Py_INIT_OK();
}
_PyInitError
_PyCoreConfig_InitPathConfig(_PyCoreConfig *config)
{
_PyPathConfig path_config = _PyPathConfig_INIT;
_PyInitError err;
err = _PyPathConfig_Calculate(&path_config, config);
if (_Py_INIT_FAILED(err)) {
goto error;
}
if (config->nmodule_search_path < 0) {
err = core_config_init_module_search_paths(config, &path_config);
if (_Py_INIT_FAILED(err)) {
goto error;
}
}
if (config->executable == NULL) {
if (copy_wstr(&config->executable,
path_config.program_full_path) < 0) {
goto no_memory;
}
}
if (config->prefix == NULL) {
if (copy_wstr(&config->prefix, path_config.prefix) < 0) {
goto no_memory;
}
}
if (config->exec_prefix == NULL) {
#ifdef MS_WINDOWS
wchar_t *exec_prefix = path_config.prefix;
#else
wchar_t *exec_prefix = path_config.exec_prefix;
#endif
if (copy_wstr(&config->exec_prefix, exec_prefix) < 0) {
goto no_memory;
}
}
#ifdef MS_WINDOWS
if (config->dll_path == NULL) {
if (copy_wstr(&config->dll_path, path_config.dll_path) < 0) {
goto no_memory;
}
}
#endif
if (config->base_prefix == NULL) {
if (copy_wstr(&config->base_prefix, config->prefix) < 0) {
goto no_memory;
}
}
if (config->base_exec_prefix == NULL) {
if (copy_wstr(&config->base_exec_prefix, config->exec_prefix) < 0) {
goto no_memory;
}
}
_PyPathConfig_Clear(&path_config);
return _Py_INIT_OK();
no_memory:
err = _Py_INIT_NO_MEMORY();
error:
_PyPathConfig_Clear(&path_config);
return err;
}
static void
pathconfig_global_init(void)
{
if (_Py_path_config.module_search_path) {
if (_Py_path_config.module_search_path != NULL) {
/* Already initialized */
return;
}
_PyInitError err;
_PyPathConfig path_config = _PyPathConfig_INIT;
_PyCoreConfig config = _PyCoreConfig_INIT;
err = _PyCoreConfig_Read(&config);
@ -113,15 +373,22 @@ pathconfig_global_init(void)
goto error;
}
err = _PyPathConfig_Init(&config);
err = _PyPathConfig_Calculate(&path_config, &config);
if (_Py_INIT_FAILED(err)) {
goto error;
}
err = _PyPathConfig_SetGlobal(&path_config);
if (_Py_INIT_FAILED(err)) {
goto error;
}
_PyPathConfig_Clear(&path_config);
_PyCoreConfig_Clear(&config);
return;
error:
_PyPathConfig_Clear(&path_config);
_PyCoreConfig_Clear(&config);
_Py_FatalInitError(err);
}
@ -142,13 +409,18 @@ Py_SetPath(const wchar_t *path)
_PyPathConfig new_config;
new_config.program_full_path = _PyMem_RawWcsdup(Py_GetProgramName());
int alloc_error = (new_config.program_full_path == NULL);
new_config.prefix = _PyMem_RawWcsdup(L"");
alloc_error |= (new_config.prefix == NULL);
#ifdef MS_WINDOWS
new_config.dll_path = _PyMem_RawWcsdup(L"");
alloc_error |= (new_config.dll_path == NULL);
#else
new_config.exec_prefix = _PyMem_RawWcsdup(L"");
alloc_error |= (new_config.exec_prefix == NULL);
#endif
new_config.module_search_path = _PyMem_RawWcsdup(path);
alloc_error |= (new_config.module_search_path == NULL);
/* steal the home and program_name values (to leave them unchanged) */
new_config.home = _Py_path_config.home;
@ -160,6 +432,10 @@ Py_SetPath(const wchar_t *path)
_Py_path_config = new_config;
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (alloc_error) {
Py_FatalError("Py_SetPath() failed: out of memory");
}
}