diff --git a/plugins/python/pyhelpers.c b/plugins/python/pyhelpers.c index d03f06bd6..894309ab2 100644 --- a/plugins/python/pyhelpers.c +++ b/plugins/python/pyhelpers.c @@ -116,13 +116,19 @@ py_create_traceback_string(PyObject *py_traceback) char* traceback = NULL; - if (py_ctx.py_traceback_module != NULL) { - PyObject *py_traceback_str_list = PyObject_CallMethod(py_ctx.py_traceback_module, "format_tb", "(O)", py_traceback); + + PyObject *py_traceback_module = PyImport_ImportModule("traceback"); + if (py_traceback_module == NULL) { + PyErr_Clear(); // do not care, we just won't show backtrace + } else { + PyObject *py_traceback_str_list = PyObject_CallMethod(py_traceback_module, "format_tb", "(O)", py_traceback); if (py_traceback_str_list != NULL) { traceback = py_join_str_list(py_traceback_str_list, ""); Py_DECREF(py_traceback_str_list); } + + Py_CLEAR(py_traceback_module); } debug_return_str(traceback ? traceback : strdup("")); diff --git a/plugins/python/pyhelpers.h b/plugins/python/pyhelpers.h index d8b2dd122..471218797 100644 --- a/plugins/python/pyhelpers.h +++ b/plugins/python/pyhelpers.h @@ -43,8 +43,7 @@ struct PythonContext sudo_printf_t sudo_log; sudo_conv_t sudo_conv; int open_plugin_count; - - PyObject *py_traceback_module; + PyThreadState *py_main_interpreter; }; extern struct PythonContext py_ctx; diff --git a/plugins/python/python_plugin_common.c b/plugins/python/python_plugin_common.c index 060ce0ce5..60566237f 100644 --- a/plugins/python/python_plugin_common.c +++ b/plugins/python/python_plugin_common.c @@ -242,15 +242,9 @@ _python_plugin_register_plugin_in_py_ctx(void) PyImport_AppendInittab("sudo", sudo_module_init); Py_InitializeEx(0); - - if (!sudo_conf_developer_mode() && sudo_module_register_importblocker() < 0) { - py_log_last_error(NULL); - debug_return_int(SUDO_RC_ERROR); - } - - py_ctx.py_traceback_module = PyImport_ImportModule("traceback"); - // if getting the traceback module fails, we just don't show tracebacks - PyErr_Clear(); + py_ctx.py_main_interpreter = PyThreadState_Get(); + } else { + PyThreadState_Swap(py_ctx.py_main_interpreter); } ++py_ctx.open_plugin_count; @@ -267,6 +261,17 @@ python_plugin_init(struct PluginContext *plugin_ctx, char * const plugin_options if (_python_plugin_register_plugin_in_py_ctx() != SUDO_RC_OK) goto cleanup; + plugin_ctx->py_interpreter = Py_NewInterpreter(); + if (plugin_ctx->py_interpreter == NULL) { + goto cleanup; + } + PyThreadState_Swap(plugin_ctx->py_interpreter); + + if (!sudo_conf_developer_mode() && sudo_module_register_importblocker() < 0) { + py_log_last_error(NULL); + debug_return_int(SUDO_RC_ERROR); + } + const char *module_path = _lookup_value(plugin_options, "ModulePath"); if (module_path == NULL) { py_sudo_log(SUDO_CONV_ERROR_MSG, "No python module path is specified. " @@ -321,13 +326,21 @@ python_plugin_deinit(struct PluginContext *plugin_ctx) Py_CLEAR(plugin_ctx->py_instance); Py_CLEAR(plugin_ctx->py_class); Py_CLEAR(plugin_ctx->py_module); + + if (plugin_ctx->py_interpreter != NULL) { + sudo_debug_printf(SUDO_DEBUG_TRACE, "deinit python interpreter for plugin\n"); + Py_EndInterpreter(plugin_ctx->py_interpreter); + } + memset(plugin_ctx, 0, sizeof(*plugin_ctx)); if (py_ctx.open_plugin_count <= 0) { - Py_CLEAR(py_ctx.py_traceback_module); - if (Py_IsInitialized()) { sudo_debug_printf(SUDO_DEBUG_NOTICE, "Closing: deinit python interpreter\n"); + + // we need to call finalize from the main interpreter + PyThreadState_Swap(py_ctx.py_main_interpreter); + Py_Finalize(); } @@ -413,6 +426,8 @@ python_plugin_close(struct PluginContext *plugin_ctx, const char *python_callbac { debug_decl(python_plugin_close, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(plugin_ctx->py_interpreter); + if (!plugin_ctx->call_close) { sudo_debug_printf(SUDO_DEBUG_INFO, "Skipping close call, because there was no command run\n"); diff --git a/plugins/python/python_plugin_common.h b/plugins/python/python_plugin_common.h index 14984f5b7..e10b3c14d 100644 --- a/plugins/python/python_plugin_common.h +++ b/plugins/python/python_plugin_common.h @@ -22,6 +22,7 @@ #include "pyhelpers.h" struct PluginContext { + PyThreadState *py_interpreter; PyObject *py_module; PyObject *py_class; PyObject *py_instance; diff --git a/plugins/python/python_plugin_group.c b/plugins/python/python_plugin_group.c index 1295cebf2..87704e06d 100644 --- a/plugins/python/python_plugin_group.c +++ b/plugins/python/python_plugin_group.c @@ -83,6 +83,7 @@ void python_plugin_group_cleanup(void) { debug_decl(python_plugin_group_cleanup, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(plugin_ctx.py_interpreter); python_plugin_deinit(&plugin_ctx); } @@ -91,6 +92,8 @@ python_plugin_group_query(const char *user, const char *group, const struct pass { debug_decl(python_plugin_group_query, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(plugin_ctx.py_interpreter); + PyObject *py_pwd = py_from_passwd(pwd); if (py_pwd == NULL) { debug_return_int(SUDO_RC_ERROR); diff --git a/plugins/python/python_plugin_io.c b/plugins/python/python_plugin_io.c index 50de682c1..2db8b3690 100644 --- a/plugins/python/python_plugin_io.c +++ b/plugins/python/python_plugin_io.c @@ -134,6 +134,8 @@ python_plugin_io_show_version(struct IOPluginContext *io_ctx, int verbose) { debug_decl(python_plugin_io_show_version, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); + if (verbose) { py_sudo_log(SUDO_CONV_INFO_MSG, "Python io plugin API version %d.%d\n", "%d.%d", SUDO_API_VERSION_GET_MAJOR(PY_IO_PLUGIN_VERSION), @@ -147,6 +149,7 @@ int python_plugin_io_log_ttyin(struct IOPluginContext *io_ctx, const char *buf, unsigned int len) { debug_decl(python_plugin_io_log_ttyin, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_ttyin), Py_BuildValue("(s#)", buf, len))); } @@ -155,6 +158,7 @@ int python_plugin_io_log_ttyout(struct IOPluginContext *io_ctx, const char *buf, unsigned int len) { debug_decl(python_plugin_io_log_ttyout, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_ttyout), Py_BuildValue("(s#)", buf, len))); } @@ -163,6 +167,7 @@ int python_plugin_io_log_stdin(struct IOPluginContext *io_ctx, const char *buf, unsigned int len) { debug_decl(python_plugin_io_log_stdin, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stdin), Py_BuildValue("(s#)", buf, len))); } @@ -171,6 +176,7 @@ int python_plugin_io_log_stdout(struct IOPluginContext *io_ctx, const char *buf, unsigned int len) { debug_decl(python_plugin_io_log_stdout, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stdout), Py_BuildValue("(s#)", buf, len))); } @@ -179,6 +185,7 @@ int python_plugin_io_log_stderr(struct IOPluginContext *io_ctx, const char *buf, unsigned int len) { debug_decl(python_plugin_io_log_stderr, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stderr), Py_BuildValue("(s#)", buf, len))); } @@ -187,6 +194,7 @@ int python_plugin_io_change_winsize(struct IOPluginContext *io_ctx, unsigned int line, unsigned int cols) { debug_decl(python_plugin_io_change_winsize, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(change_winsize), Py_BuildValue("(ii)", line, cols))); } @@ -195,6 +203,7 @@ int python_plugin_io_log_suspend(struct IOPluginContext *io_ctx, int signo) { debug_decl(python_plugin_io_log_suspend, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter); debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_suspend), Py_BuildValue("(i)", signo))); } diff --git a/plugins/python/python_plugin_policy.c b/plugins/python/python_plugin_policy.c index caf63cef7..b041e5b49 100644 --- a/plugins/python/python_plugin_policy.c +++ b/plugins/python/python_plugin_policy.c @@ -96,6 +96,8 @@ python_plugin_policy_check(int argc, char * const argv[], debug_decl(python_plugin_policy_check, PYTHON_DEBUG_CALLBACKS); int rc = SUDO_RC_ERROR; + PyThreadState_Swap(plugin_ctx.py_interpreter); + *command_info_out = *argv_out = *user_env_out = NULL; PyObject *py_argv = py_str_array_to_tuple_with_count(argc, argv); @@ -169,6 +171,8 @@ python_plugin_policy_list(int argc, char * const argv[], int verbose, const char { debug_decl(python_plugin_policy_list, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(plugin_ctx.py_interpreter); + PyObject *py_argv = py_str_array_to_tuple_with_count(argc, argv); if (py_argv == NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: Failed to create argv argument for the python call\n", __PRETTY_FUNCTION__); @@ -187,6 +191,8 @@ python_plugin_policy_version(int verbose) { debug_decl(python_plugin_policy_version, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(plugin_ctx.py_interpreter); + if (verbose) { py_sudo_log(SUDO_CONV_INFO_MSG, "Python policy plugin API version %d.%d\n", "%d.%d", SUDO_API_VERSION_GET_MAJOR(PY_POLICY_PLUGIN_VERSION), @@ -200,6 +206,7 @@ int python_plugin_policy_validate(void) { debug_decl(python_plugin_policy_validate, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(plugin_ctx.py_interpreter); debug_return_int(python_plugin_api_rc_call(&plugin_ctx, CALLBACK_PYNAME(validate), NULL)); } @@ -207,6 +214,7 @@ void python_plugin_policy_invalidate(int remove) { debug_decl(python_plugin_policy_invalidate, PYTHON_DEBUG_CALLBACKS); + PyThreadState_Swap(plugin_ctx.py_interpreter); python_plugin_api_rc_call(&plugin_ctx, CALLBACK_PYNAME(invalidate), Py_BuildValue("(i)", remove)); debug_return; @@ -217,6 +225,7 @@ python_plugin_policy_init_session(struct passwd *pwd, char **user_env[]) { debug_decl(python_plugin_policy_init_session, PYTHON_DEBUG_CALLBACKS); int rc = SUDO_RC_ERROR; + PyThreadState_Swap(plugin_ctx.py_interpreter); PyObject *py_pwd = NULL, *py_user_env = NULL, *py_result = NULL; py_pwd = py_from_passwd(pwd);