mirror of https://github.com/python/cpython
gh-112292 : Catch import error conditions with readline hooks (gh-112313)
Prevents a segmentation fault in registered hooks for the readline library, but only when the readline module is loaded inside an isolated sub interpreter. The module is single-phase init so loading it fails, but not until the module init function has already run, where the readline hooks get registered. The readlinestate_global macro was error-prone to PyImport_FindModule returning NULL and crashing in about 18 places. I could reproduce 1 easily, but this PR replaces the macro with a function and adds error conditions to the other functions.
This commit is contained in:
parent
2e632fa07d
commit
154f099e61
|
@ -0,0 +1,2 @@
|
|||
Fix a crash in :mod:`readline` when imported from a sub interpreter. Patch
|
||||
by Anthony Shaw
|
|
@ -147,8 +147,19 @@ readline_free(void *m)
|
|||
|
||||
static PyModuleDef readlinemodule;
|
||||
|
||||
#define readlinestate_global ((readlinestate *)PyModule_GetState(PyState_FindModule(&readlinemodule)))
|
||||
|
||||
static inline readlinestate*
|
||||
get_hook_module_state(void)
|
||||
{
|
||||
PyObject *mod = PyState_FindModule(&readlinemodule);
|
||||
if (mod == NULL){
|
||||
PyErr_Clear();
|
||||
return NULL;
|
||||
}
|
||||
Py_INCREF(mod);
|
||||
readlinestate *state = get_readline_state(mod);
|
||||
Py_DECREF(mod);
|
||||
return state;
|
||||
}
|
||||
|
||||
/* Convert to/from multibyte C strings */
|
||||
|
||||
|
@ -438,14 +449,15 @@ readline_set_completion_display_matches_hook_impl(PyObject *module,
|
|||
PyObject *function)
|
||||
/*[clinic end generated code: output=516e5cb8db75a328 input=4f0bfd5ab0179a26]*/
|
||||
{
|
||||
readlinestate *state = get_readline_state(module);
|
||||
PyObject *result = set_hook("completion_display_matches_hook",
|
||||
&readlinestate_global->completion_display_matches_hook,
|
||||
&state->completion_display_matches_hook,
|
||||
function);
|
||||
#ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK
|
||||
/* We cannot set this hook globally, since it replaces the
|
||||
default completion display. */
|
||||
rl_completion_display_matches_hook =
|
||||
readlinestate_global->completion_display_matches_hook ?
|
||||
state->completion_display_matches_hook ?
|
||||
#if defined(HAVE_RL_COMPDISP_FUNC_T)
|
||||
(rl_compdisp_func_t *)on_completion_display_matches_hook : 0;
|
||||
#else
|
||||
|
@ -472,7 +484,8 @@ static PyObject *
|
|||
readline_set_startup_hook_impl(PyObject *module, PyObject *function)
|
||||
/*[clinic end generated code: output=02cd0e0c4fa082ad input=7783b4334b26d16d]*/
|
||||
{
|
||||
return set_hook("startup_hook", &readlinestate_global->startup_hook,
|
||||
readlinestate *state = get_readline_state(module);
|
||||
return set_hook("startup_hook", &state->startup_hook,
|
||||
function);
|
||||
}
|
||||
|
||||
|
@ -497,7 +510,8 @@ static PyObject *
|
|||
readline_set_pre_input_hook_impl(PyObject *module, PyObject *function)
|
||||
/*[clinic end generated code: output=fe1a96505096f464 input=4f3eaeaf7ce1fdbe]*/
|
||||
{
|
||||
return set_hook("pre_input_hook", &readlinestate_global->pre_input_hook,
|
||||
readlinestate *state = get_readline_state(module);
|
||||
return set_hook("pre_input_hook", &state->pre_input_hook,
|
||||
function);
|
||||
}
|
||||
#endif
|
||||
|
@ -530,7 +544,8 @@ static PyObject *
|
|||
readline_get_begidx_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=362616ee8ed1b2b1 input=e083b81c8eb4bac3]*/
|
||||
{
|
||||
return Py_NewRef(readlinestate_global->begidx);
|
||||
readlinestate *state = get_readline_state(module);
|
||||
return Py_NewRef(state->begidx);
|
||||
}
|
||||
|
||||
/* Get the ending index for the scope of the tab-completion */
|
||||
|
@ -545,7 +560,8 @@ static PyObject *
|
|||
readline_get_endidx_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=7f763350b12d7517 input=d4c7e34a625fd770]*/
|
||||
{
|
||||
return Py_NewRef(readlinestate_global->endidx);
|
||||
readlinestate *state = get_readline_state(module);
|
||||
return Py_NewRef(state->endidx);
|
||||
}
|
||||
|
||||
/* Set the tab-completion word-delimiters that readline uses */
|
||||
|
@ -772,7 +788,8 @@ static PyObject *
|
|||
readline_set_completer_impl(PyObject *module, PyObject *function)
|
||||
/*[clinic end generated code: output=171a2a60f81d3204 input=51e81e13118eb877]*/
|
||||
{
|
||||
return set_hook("completer", &readlinestate_global->completer, function);
|
||||
readlinestate *state = get_readline_state(module);
|
||||
return set_hook("completer", &state->completer, function);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
@ -785,10 +802,11 @@ static PyObject *
|
|||
readline_get_completer_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=6e6bbd8226d14475 input=6457522e56d70d13]*/
|
||||
{
|
||||
if (readlinestate_global->completer == NULL) {
|
||||
readlinestate *state = get_readline_state(module);
|
||||
if (state->completer == NULL) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return Py_NewRef(readlinestate_global->completer);
|
||||
return Py_NewRef(state->completer);
|
||||
}
|
||||
|
||||
/* Private function to get current length of history. XXX It may be
|
||||
|
@ -1026,7 +1044,12 @@ on_startup_hook(void)
|
|||
{
|
||||
int r;
|
||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
r = on_hook(readlinestate_global->startup_hook);
|
||||
readlinestate *state = get_hook_module_state();
|
||||
if (state == NULL) {
|
||||
PyGILState_Release(gilstate);
|
||||
return -1;
|
||||
}
|
||||
r = on_hook(state->startup_hook);
|
||||
PyGILState_Release(gilstate);
|
||||
return r;
|
||||
}
|
||||
|
@ -1043,7 +1066,12 @@ on_pre_input_hook(void)
|
|||
{
|
||||
int r;
|
||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
r = on_hook(readlinestate_global->pre_input_hook);
|
||||
readlinestate *state = get_hook_module_state();
|
||||
if (state == NULL) {
|
||||
PyGILState_Release(gilstate);
|
||||
return -1;
|
||||
}
|
||||
r = on_hook(state->pre_input_hook);
|
||||
PyGILState_Release(gilstate);
|
||||
return r;
|
||||
}
|
||||
|
@ -1060,6 +1088,11 @@ on_completion_display_matches_hook(char **matches,
|
|||
int i;
|
||||
PyObject *sub, *m=NULL, *s=NULL, *r=NULL;
|
||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
readlinestate *state = get_hook_module_state();
|
||||
if (state == NULL) {
|
||||
PyGILState_Release(gilstate);
|
||||
return;
|
||||
}
|
||||
m = PyList_New(num_matches);
|
||||
if (m == NULL)
|
||||
goto error;
|
||||
|
@ -1070,7 +1103,7 @@ on_completion_display_matches_hook(char **matches,
|
|||
PyList_SET_ITEM(m, i, s);
|
||||
}
|
||||
sub = decode(matches[0]);
|
||||
r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook,
|
||||
r = PyObject_CallFunction(state->completion_display_matches_hook,
|
||||
"NNi", sub, m, max_length);
|
||||
|
||||
m=NULL;
|
||||
|
@ -1118,12 +1151,17 @@ static char *
|
|||
on_completion(const char *text, int state)
|
||||
{
|
||||
char *result = NULL;
|
||||
if (readlinestate_global->completer != NULL) {
|
||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
readlinestate *module_state = get_hook_module_state();
|
||||
if (module_state == NULL) {
|
||||
PyGILState_Release(gilstate);
|
||||
return NULL;
|
||||
}
|
||||
if (module_state->completer != NULL) {
|
||||
PyObject *r = NULL, *t;
|
||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
rl_attempted_completion_over = 1;
|
||||
t = decode(text);
|
||||
r = PyObject_CallFunction(readlinestate_global->completer, "Ni", t, state);
|
||||
r = PyObject_CallFunction(module_state->completer, "Ni", t, state);
|
||||
if (r == NULL)
|
||||
goto error;
|
||||
if (r == Py_None) {
|
||||
|
@ -1145,6 +1183,7 @@ on_completion(const char *text, int state)
|
|||
PyGILState_Release(gilstate);
|
||||
return result;
|
||||
}
|
||||
PyGILState_Release(gilstate);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1160,6 +1199,7 @@ flex_complete(const char *text, int start, int end)
|
|||
size_t start_size, end_size;
|
||||
wchar_t *s;
|
||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
readlinestate *state = get_hook_module_state();
|
||||
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
|
||||
rl_completion_append_character ='\0';
|
||||
#endif
|
||||
|
@ -1187,10 +1227,12 @@ flex_complete(const char *text, int start, int end)
|
|||
end = start + (int)end_size;
|
||||
|
||||
done:
|
||||
Py_XDECREF(readlinestate_global->begidx);
|
||||
Py_XDECREF(readlinestate_global->endidx);
|
||||
readlinestate_global->begidx = PyLong_FromLong((long) start);
|
||||
readlinestate_global->endidx = PyLong_FromLong((long) end);
|
||||
if (state) {
|
||||
Py_XDECREF(state->begidx);
|
||||
Py_XDECREF(state->endidx);
|
||||
state->begidx = PyLong_FromLong((long) start);
|
||||
state->endidx = PyLong_FromLong((long) end);
|
||||
}
|
||||
result = completion_matches((char *)text, *on_completion);
|
||||
PyGILState_Release(gilstate);
|
||||
return result;
|
||||
|
@ -1511,12 +1553,17 @@ PyInit_readline(void)
|
|||
}
|
||||
|
||||
mod_state = (readlinestate *) PyModule_GetState(m);
|
||||
if (mod_state == NULL){
|
||||
goto error;
|
||||
}
|
||||
PyOS_ReadlineFunctionPointer = call_readline;
|
||||
if (setup_readline(mod_state) < 0) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()){
|
||||
goto error;
|
||||
}
|
||||
return m;
|
||||
|
||||
error:
|
||||
|
|
Loading…
Reference in New Issue