diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2023-02-15 18:16:00 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-15 18:16:00 -0700 |
commit | 89ac665891dec1988bedec2ce9b2c4d016502a49 (patch) | |
tree | 246997ab21e8b587b8a3f58ac93d7b278c9d0938 /Python | |
parent | 3dea4ba6c1b9237893d23574f931f33c940b74e8 (diff) | |
download | cpython-89ac665891dec1988bedec2ce9b2c4d016502a49.tar.gz cpython-89ac665891dec1988bedec2ce9b2c4d016502a49.zip |
gh-98627: Add an Optional Check for Extension Module Subinterpreter Compatibility (gh-99040)
Enforcing (optionally) the restriction set by PEP 489 makes sense. Furthermore, this sets the stage for a potential restriction related to a per-interpreter GIL.
This change includes the following:
* add tests for extension module subinterpreter compatibility
* add _PyInterpreterConfig.check_multi_interp_extensions
* add Py_RTFLAGS_MULTI_INTERP_EXTENSIONS
* add _PyImport_CheckSubinterpIncompatibleExtensionAllowed()
* fail iff the module does not implement multi-phase init and the current interpreter is configured to check
https://github.com/python/cpython/issues/98627
Diffstat (limited to 'Python')
-rw-r--r-- | Python/clinic/import.c.h | 33 | ||||
-rw-r--r-- | Python/import.c | 88 | ||||
-rw-r--r-- | Python/importdl.c | 5 | ||||
-rw-r--r-- | Python/pylifecycle.c | 4 |
4 files changed, 120 insertions, 10 deletions
diff --git a/Python/clinic/import.c.h b/Python/clinic/import.c.h index 819fb1c75c1..cb74be6a422 100644 --- a/Python/clinic/import.c.h +++ b/Python/clinic/import.c.h @@ -442,6 +442,37 @@ exit: return return_value; } +PyDoc_STRVAR(_imp__override_multi_interp_extensions_check__doc__, +"_override_multi_interp_extensions_check($module, override, /)\n" +"--\n" +"\n" +"(internal-only) Override PyInterpreterConfig.check_multi_interp_extensions.\n" +"\n" +"(-1: \"never\", 1: \"always\", 0: no override)"); + +#define _IMP__OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK_METHODDEF \ + {"_override_multi_interp_extensions_check", (PyCFunction)_imp__override_multi_interp_extensions_check, METH_O, _imp__override_multi_interp_extensions_check__doc__}, + +static PyObject * +_imp__override_multi_interp_extensions_check_impl(PyObject *module, + int override); + +static PyObject * +_imp__override_multi_interp_extensions_check(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int override; + + override = _PyLong_AsInt(arg); + if (override == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _imp__override_multi_interp_extensions_check_impl(module, override); + +exit: + return return_value; +} + #if defined(HAVE_DYNAMIC_LOADING) PyDoc_STRVAR(_imp_create_dynamic__doc__, @@ -617,4 +648,4 @@ exit: #ifndef _IMP_EXEC_DYNAMIC_METHODDEF #define _IMP_EXEC_DYNAMIC_METHODDEF #endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */ -/*[clinic end generated code: output=806352838c3f7008 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b18d46e0036eff49 input=a9049054013a1b77]*/ diff --git a/Python/import.c b/Python/import.c index 87981668a30..ec126f28b85 100644 --- a/Python/import.c +++ b/Python/import.c @@ -74,6 +74,8 @@ static struct _inittab *inittab_copy = NULL; (interp)->imports.modules_by_index #define IMPORTLIB(interp) \ (interp)->imports.importlib +#define OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) \ + (interp)->imports.override_multi_interp_extensions_check #define OVERRIDE_FROZEN_MODULES(interp) \ (interp)->imports.override_frozen_modules #ifdef HAVE_DLOPEN @@ -816,6 +818,38 @@ _extensions_cache_clear_all(void) Py_CLEAR(EXTENSIONS); } + +static bool +check_multi_interp_extensions(PyInterpreterState *interp) +{ + int override = OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp); + if (override < 0) { + return false; + } + else if (override > 0) { + return true; + } + else if (_PyInterpreterState_HasFeature( + interp, Py_RTFLAGS_MULTI_INTERP_EXTENSIONS)) { + return true; + } + return false; +} + +int +_PyImport_CheckSubinterpIncompatibleExtensionAllowed(const char *name) +{ + PyInterpreterState *interp = _PyInterpreterState_Get(); + if (check_multi_interp_extensions(interp)) { + assert(!_Py_IsMainInterpreter(interp)); + PyErr_Format(PyExc_ImportError, + "module %s does not support loading in subinterpreters", + name); + return -1; + } + return 0; +} + static int fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) { @@ -3297,6 +3331,34 @@ _imp__override_frozen_modules_for_tests_impl(PyObject *module, int override) Py_RETURN_NONE; } +/*[clinic input] +_imp._override_multi_interp_extensions_check + + override: int + / + +(internal-only) Override PyInterpreterConfig.check_multi_interp_extensions. + +(-1: "never", 1: "always", 0: no override) +[clinic start generated code]*/ + +static PyObject * +_imp__override_multi_interp_extensions_check_impl(PyObject *module, + int override) +/*[clinic end generated code: output=3ff043af52bbf280 input=e086a2ea181f92ae]*/ +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_Py_IsMainInterpreter(interp)) { + PyErr_SetString(PyExc_RuntimeError, + "_imp._override_multi_interp_extensions_check() " + "cannot be used in the main interpreter"); + return NULL; + } + int oldvalue = OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp); + OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) = override; + return PyLong_FromLong(oldvalue); +} + #ifdef HAVE_DYNAMIC_LOADING /*[clinic input] @@ -3329,18 +3391,23 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) PyThreadState *tstate = _PyThreadState_GET(); mod = import_find_extension(tstate, name, path); - if (mod != NULL || PyErr_Occurred()) { - Py_DECREF(name); - Py_DECREF(path); - return mod; + if (mod != NULL) { + const char *name_buf = PyUnicode_AsUTF8(name); + assert(name_buf != NULL); + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + Py_DECREF(mod); + mod = NULL; + } + goto finally; + } + else if (PyErr_Occurred()) { + goto finally; } if (file != NULL) { fp = _Py_fopen_obj(path, "r"); if (fp == NULL) { - Py_DECREF(name); - Py_DECREF(path); - return NULL; + goto finally; } } else @@ -3348,10 +3415,12 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) mod = _PyImport_LoadDynamicModuleWithSpec(spec, fp); - Py_DECREF(name); - Py_DECREF(path); if (fp) fclose(fp); + +finally: + Py_DECREF(name); + Py_DECREF(path); return mod; } @@ -3436,6 +3505,7 @@ static PyMethodDef imp_methods[] = { _IMP_IS_FROZEN_METHODDEF _IMP__FROZEN_MODULE_NAMES_METHODDEF _IMP__OVERRIDE_FROZEN_MODULES_FOR_TESTS_METHODDEF + _IMP__OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK_METHODDEF _IMP_CREATE_DYNAMIC_METHODDEF _IMP_EXEC_DYNAMIC_METHODDEF _IMP_EXEC_BUILTIN_METHODDEF diff --git a/Python/importdl.c b/Python/importdl.c index 6dafb454148..3a3a30ddbdc 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_call.h" +#include "pycore_import.h" #include "pycore_pystate.h" #include "pycore_runtime.h" @@ -203,6 +204,10 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) /* Fall back to single-phase init mechanism */ + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + goto error; + } + if (hook_prefix == nonascii_prefix) { /* don't allow legacy init for non-ASCII module names */ PyErr_Format( diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 281035dafa9..e80dd30c89d 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -565,6 +565,10 @@ init_interp_settings(PyInterpreterState *interp, const _PyInterpreterConfig *con if (config->allow_daemon_threads) { interp->feature_flags |= Py_RTFLAGS_DAEMON_THREADS; } + + if (config->check_multi_interp_extensions) { + interp->feature_flags |= Py_RTFLAGS_MULTI_INTERP_EXTENSIONS; + } } |