From 6a33b20186d9fc957a40e5a3b0b9749967a87c16 Mon Sep 17 00:00:00 2001 From: Marien Zwart Date: Tue, 7 Mar 2017 00:32:55 +1100 Subject: [PATCH] Try not to crash when using readline with high FDs. We used the input FD for readline with select(), which crashes if it is >1024. Fix it by adding an implementation based on poll(). This does not fix it on all platforms, because poll() does not work correctly on terminals on some platforms. For now, only use the new implemetation on Linux. This also does not fix it on all versions of readline, as some of them use select() internally. Those crash the same way we used to. Fixes issue 29700. --- Modules/readline.c | 50 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index ad5903cfb51a54..874ec25dd8e86a 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -11,6 +11,10 @@ #include #include +#ifdef HAVE_POLL_H +#include +#endif + #if defined(HAVE_SETLOCALE) /* GNU readline() mistakenly sets the LC_CTYPE locale. * This is evil. Only the user or the app's main() should do this! @@ -996,7 +1000,15 @@ setup_readline(void) /* Wrapper around GNU readline that handles signals differently. */ -#if defined(HAVE_RL_CALLBACK) && defined(HAVE_SELECT) +#if defined(HAVE_RL_CALLBACK) && (defined(HAVE_SELECT) || defined(HAVE_POLL)) +/* Issue #26870: poll() does not work with terminals on all OSes. + * Be conservative and use select() on non-Linux. + * TODO(marienz) "glibc" is probably the wrong thing to check, + * but I cannot find an existing "OS is Linux" #define... + */ +#if defined(HAVE_POLL) && defined(__GLIBC__) +#define READLINE_USE_POLL 1 +#endif static char *completed_input_string; static void @@ -1010,7 +1022,11 @@ static char * readline_until_enter_or_signal(char *prompt, int *signal) { char * not_done_reading = ""; +#ifdef READLINE_USE_POLL + struct pollfd pollfd; +#else fd_set selectset; +#endif *signal = 0; #ifdef HAVE_RL_CATCH_SIGNAL @@ -1018,7 +1034,23 @@ readline_until_enter_or_signal(char *prompt, int *signal) #endif rl_callback_handler_install (prompt, rlhandler); +#ifdef READLINE_USE_POLL + pollfd.fd = fileno(rl_instream); + pollfd.events = POLLIN; +#else FD_ZERO(&selectset); + if (fileno(rl_instream) >= FD_SETSIZE) { +#ifdef WITH_THREAD + PyEval_RestoreThread(_PyOS_ReadlineTState); +#endif + PyErr_SetString(PyExc_ValueError, "input fd too large for select"); +#ifdef WITH_THREAD + PyEval_SaveThread(); +#endif + *signal = -1; + return NULL; + } +#endif completed_input_string = not_done_reading; @@ -1026,13 +1058,19 @@ readline_until_enter_or_signal(char *prompt, int *signal) int has_input = 0; while (!has_input) - { struct timeval timeout = {0, 100000}; /* 0.1 seconds */ - + { +#ifdef READLINE_USE_POLL + int timeout = -1; + if (PyOS_InputHook) + timeout = 100; /* 0.1 seconds */ +#else + struct timeval timeout = {0, 100000}; /* 0.1 seconds */ /* [Bug #1552726] Only limit the pause if an input hook has been defined. */ struct timeval *timeoutp = NULL; if (PyOS_InputHook) timeoutp = &timeout; +#endif #ifdef HAVE_RL_RESIZE_TERMINAL /* Update readline's view of the window size after SIGWINCH */ if (sigwinch_received) { @@ -1040,10 +1078,14 @@ readline_until_enter_or_signal(char *prompt, int *signal) rl_resize_terminal(); } #endif +#ifdef READLINE_USE_POLL + has_input = poll(&pollfd, 1, timeout); +#else FD_SET(fileno(rl_instream), &selectset); /* select resets selectset if no input was available */ has_input = select(fileno(rl_instream) + 1, &selectset, NULL, NULL, timeoutp); +#endif if(PyOS_InputHook) PyOS_InputHook(); } @@ -1114,7 +1156,7 @@ readline_until_enter_or_signal(char *prompt, int *signal) return p; } -#endif /*defined(HAVE_RL_CALLBACK) && defined(HAVE_SELECT) */ +#endif /*defined(HAVE_RL_CALLBACK) && (defined(HAVE_SELECT) || defined(HAVE_POLL)) */ static char *