diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2015-07-18 20:03:34 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2015-07-18 20:03:34 +0200 |
commit | 3b2ee85b04f8cfabb10b42daf41f91dc95d368eb (patch) | |
tree | 8ae678e325b807da9c3b587a58b6e92c481ea895 | |
parent | 23983b125aa37a87ee439cd9b6f7f417e8b2c079 (diff) | |
download | weechat-3b2ee85b04f8cfabb10b42daf41f91dc95d368eb.zip |
core: fix crash if a file descriptor used in hook_fd() is too high (> 1024 on Linux/BSD) (closes #465)
The calls to select() are replaced by poll(), which doesn't have limitation
on file descriptor number.
-rw-r--r-- | ChangeLog.asciidoc | 2 | ||||
-rw-r--r-- | doc/en/weechat_plugin_api.en.asciidoc | 3 | ||||
-rw-r--r-- | doc/fr/weechat_plugin_api.fr.asciidoc | 3 | ||||
-rw-r--r-- | doc/it/weechat_plugin_api.it.asciidoc | 5 | ||||
-rw-r--r-- | doc/ja/weechat_plugin_api.ja.asciidoc | 4 | ||||
-rw-r--r-- | src/core/wee-hook.c | 257 | ||||
-rw-r--r-- | src/core/wee-hook.h | 6 | ||||
-rw-r--r-- | src/core/wee-network.c | 13 | ||||
-rw-r--r-- | src/gui/curses/gui-curses-main.c | 21 |
9 files changed, 182 insertions, 132 deletions
diff --git a/ChangeLog.asciidoc b/ChangeLog.asciidoc index ae5315c33..62bb574ab 100644 --- a/ChangeLog.asciidoc +++ b/ChangeLog.asciidoc @@ -19,6 +19,8 @@ https://weechat.org/files/releasenotes/ReleaseNotes-devel.html[release notes] === New features +* core: fix crash if a file descriptor used in hook_fd() is too high + (> 1024 on Linux/BSD) (closes #465) * core: add option weechat.look.confirm_upgrade (closes #463) * core: allow ctrl-C to exit WeeChat when the passphrase is asked on startup (closes #452) diff --git a/doc/en/weechat_plugin_api.en.asciidoc b/doc/en/weechat_plugin_api.en.asciidoc index c9b12cb88..222153ffd 100644 --- a/doc/en/weechat_plugin_api.en.asciidoc +++ b/doc/en/weechat_plugin_api.en.asciidoc @@ -7228,6 +7228,8 @@ hook = weechat.hook_timer(20 * 1000, 0, 0, "my_timer_cb", "") ==== hook_fd +_Updated in 1.3._ + Hook a file descriptor (file or socket). Prototype: @@ -7249,6 +7251,7 @@ Arguments: * 'flag_read': 1 = catch read event, 0 = ignore * 'flag_write': 1 = catch write event, 0 = ignore * 'flag_exception': 1 = catch exception event, 0 = ignore + (_WeeChat ≥ 1.3_: this argument is ignored and not used any more) * 'callback': function called a selected event occurs for file (or socket), arguments and return value: ** 'void *data': pointer diff --git a/doc/fr/weechat_plugin_api.fr.asciidoc b/doc/fr/weechat_plugin_api.fr.asciidoc index 79b5e7e64..d24c4f53e 100644 --- a/doc/fr/weechat_plugin_api.fr.asciidoc +++ b/doc/fr/weechat_plugin_api.fr.asciidoc @@ -7352,6 +7352,8 @@ hook = weechat.hook_timer(20 * 1000, 0, 0, "my_timer_cb", "") ==== hook_fd +_Mis à jour dans la 1.3._ + Accrocher un descripteur de fichier (fichier ou socket). Prototype : @@ -7373,6 +7375,7 @@ Paramètres : * 'flag_read' : 1 = intercepter un évènement de lecture, 0 = ignorer * 'flag_write' : 1 = intercepter un évènement d'écriture, 0 = ignorer * 'flag_exception' : 1 = intercepter un évènement d'exception, 0 = ignorer + (_WeeChat ≥ 1.3_ : ce paramètre est ignoré et n'est plus utilisé) * 'callback' : fonction appelée lorsqu'un des évènements sélectionnés se produit pour le fichier (ou le socket), paramètres et valeur de retour : ** 'void *data' : pointeur diff --git a/doc/it/weechat_plugin_api.it.asciidoc b/doc/it/weechat_plugin_api.it.asciidoc index 46a547249..e14081dbb 100644 --- a/doc/it/weechat_plugin_api.it.asciidoc +++ b/doc/it/weechat_plugin_api.it.asciidoc @@ -7391,6 +7391,9 @@ hook = weechat.hook_timer(20 * 1000, 0, 0, "my_timer_cb", "") ==== hook_fd +// TRANSLATION MISSING +_Updated in 1.3._ + Hook su un descrittore file (file oppure socket). Prototipo: @@ -7411,7 +7414,9 @@ Argomenti: * 'fd': descrittore file * 'flag_read': 1 = cattura l'evento lettura (read), 0 = ignora * 'flag_write': 1 = cattura l'evento scrittura (write), 0 = ignora +// TRANSLATION MISSING * 'flag_exception': 1 = cattura l'eccezione evento (event), 0 = ignora + (_WeeChat ≥ 1.3_: this argument is ignored and not used any more) * 'callback': funzione che chiama un evento selezionato che si verifica per un file (o un socket), argomenti e valore restituito: ** 'void *data': puntatore diff --git a/doc/ja/weechat_plugin_api.ja.asciidoc b/doc/ja/weechat_plugin_api.ja.asciidoc index ff5d64ff0..df591c665 100644 --- a/doc/ja/weechat_plugin_api.ja.asciidoc +++ b/doc/ja/weechat_plugin_api.ja.asciidoc @@ -7227,6 +7227,8 @@ hook = weechat.hook_timer(20 * 1000, 0, 0, "my_timer_cb", "") ==== hook_fd +_バージョン 1.3 で更新。_ + ファイルディスクリプタ (ファイルやソケット) をフック。 プロトタイプ: @@ -7247,7 +7249,9 @@ struct t_hook *weechat_hook_fd (int fd, * 'fd': ファイルディスクリプタ * 'flag_read': 1 = 読み込みイベントをキャッチ、0 = 無視 * 'flag_write': 1 = 書き込みイベントをキャッチ、0 = 無視 +// TRANSLATION MISSING * 'flag_exception': 1 = 例外イベントをキャッチ、0 = 無視 + (_WeeChat ≥ 1.3_: this argument is ignored and not used any more) * 'callback': ファイル (またはソケット) に対してキャッチしたいイベントが発生した場合に実行する関数、 引数と戻り値: ** 'void *data': ポインタ diff --git a/src/core/wee-hook.c b/src/core/wee-hook.c index f6bbdc097..c0c195eef 100644 --- a/src/core/wee-hook.c +++ b/src/core/wee-hook.c @@ -32,7 +32,7 @@ #include <time.h> #include <sys/types.h> #include <sys/wait.h> -#include <sys/select.h> +#include <poll.h> #include <signal.h> #include <fcntl.h> #include <errno.h> @@ -74,6 +74,9 @@ int hook_exec_recursion = 0; /* 1 when a hook is executed */ time_t hook_last_system_time = 0; /* used to detect system clock skew */ int real_delete_pending = 0; /* 1 if some hooks must be deleted */ +struct pollfd *hook_fd_pollfd = NULL; /* file descriptors for poll() */ +int hook_fd_pollfd_count = 0; /* number of file descriptors */ + void hook_process_run (struct t_hook *hook_process); @@ -122,6 +125,41 @@ hook_search_type (const char *type) } /* + * Reallocates the "struct pollfd" array for poll(). + */ + +void +hook_fd_realloc_pollfd () +{ + struct pollfd *ptr_pollfd; + int count; + + if (hooks_count[HOOK_TYPE_FD] == hook_fd_pollfd_count) + return; + + count = hooks_count[HOOK_TYPE_FD]; + + if (count == 0) + { + if (hook_fd_pollfd) + { + free (hook_fd_pollfd); + hook_fd_pollfd = NULL; + } + } + else + { + ptr_pollfd = realloc (hook_fd_pollfd, + count * sizeof (struct pollfd)); + if (!ptr_pollfd) + return; + hook_fd_pollfd = ptr_pollfd; + } + + hook_fd_pollfd_count = count; +} + +/* * Searches for position of hook in list (to keep hooks sorted). * * Hooks are sorted by priority, except commands which are sorted by command @@ -208,6 +246,9 @@ hook_add_to_list (struct t_hook *new_hook) hooks_count[new_hook->type]++; hooks_count_total++; + + if (new_hook->type == HOOK_TYPE_FD) + hook_fd_realloc_pollfd (); } /* @@ -241,6 +282,9 @@ hook_remove_from_list (struct t_hook *hook) hooks_count[type]--; hooks_count_total--; + + if (type == HOOK_TYPE_FD) + hook_fd_realloc_pollfd (); } /* @@ -1087,73 +1131,81 @@ hook_timer_check_system_clock () } /* - * Sets time until next timeout. + * Returns time until next timeout (in milliseconds). */ -void -hook_timer_time_to_next (struct timeval *tv_timeout) +int +hook_timer_get_time_to_next () { struct t_hook *ptr_hook; - int found; - struct timeval tv_now; + int found, timeout; + struct timeval tv_now, tv_timeout; long diff_usec; hook_timer_check_system_clock (); found = 0; - tv_timeout->tv_sec = 0; - tv_timeout->tv_usec = 0; + tv_timeout.tv_sec = 0; + tv_timeout.tv_usec = 0; for (ptr_hook = weechat_hooks[HOOK_TYPE_TIMER]; ptr_hook; ptr_hook = ptr_hook->next_hook) { if (!ptr_hook->deleted && (!found - || (util_timeval_cmp (&HOOK_TIMER(ptr_hook, next_exec), tv_timeout) < 0))) + || (util_timeval_cmp (&HOOK_TIMER(ptr_hook, next_exec), + &tv_timeout) < 0))) { found = 1; - tv_timeout->tv_sec = HOOK_TIMER(ptr_hook, next_exec).tv_sec; - tv_timeout->tv_usec = HOOK_TIMER(ptr_hook, next_exec).tv_usec; + tv_timeout.tv_sec = HOOK_TIMER(ptr_hook, next_exec).tv_sec; + tv_timeout.tv_usec = HOOK_TIMER(ptr_hook, next_exec).tv_usec; } } /* no timeout found, return 2 seconds by default */ if (!found) { - tv_timeout->tv_sec = 2; - tv_timeout->tv_usec = 0; - return; + tv_timeout.tv_sec = 2; + tv_timeout.tv_usec = 0; + goto end; } gettimeofday (&tv_now, NULL); /* next timeout is past date! */ - if (util_timeval_cmp (tv_timeout, &tv_now) < 0) + if (util_timeval_cmp (&tv_timeout, &tv_now) < 0) { - tv_timeout->tv_sec = 0; - tv_timeout->tv_usec = 0; - return; + tv_timeout.tv_sec = 0; + tv_timeout.tv_usec = 0; + goto end; } - tv_timeout->tv_sec = tv_timeout->tv_sec - tv_now.tv_sec; - diff_usec = tv_timeout->tv_usec - tv_now.tv_usec; + tv_timeout.tv_sec = tv_timeout.tv_sec - tv_now.tv_sec; + diff_usec = tv_timeout.tv_usec - tv_now.tv_usec; if (diff_usec >= 0) - tv_timeout->tv_usec = diff_usec; + { + tv_timeout.tv_usec = diff_usec; + } else { - tv_timeout->tv_sec--; - tv_timeout->tv_usec = 1000000 + diff_usec; + tv_timeout.tv_sec--; + tv_timeout.tv_usec = 1000000 + diff_usec; } /* * to detect clock skew, we ensure there's a call to timers every * 2 seconds max */ - if (tv_timeout->tv_sec > 2) + if (tv_timeout.tv_sec >= 2) { - tv_timeout->tv_sec = 2; - tv_timeout->tv_usec = 0; + tv_timeout.tv_sec = 2; + tv_timeout.tv_usec = 0; } + +end: + /* return a number of milliseconds */ + timeout = (tv_timeout.tv_sec * 1000) + (tv_timeout.tv_usec / 1000); + return (timeout < 1) ? 1 : timeout; } /* @@ -1282,18 +1334,19 @@ hook_fd (struct t_weechat_plugin *plugin, int fd, int flag_read, } /* - * Fills sets according to fd hooked. - * - * Returns highest fd set. + * Executes fd hooks: + * - poll() on fie descriptors + * - call of hook fd callbacks if needed. */ -int -hook_fd_set (fd_set *read_fds, fd_set *write_fds, fd_set *exception_fds) +void +hook_fd_exec () { - struct t_hook *ptr_hook; - int max_fd; + int i, num_fd, timeout, ready, found; + struct t_hook *ptr_hook, *next_hook; - max_fd = 0; + /* build an array of "struct pollfd" for poll() */ + num_fd = 0; for (ptr_hook = weechat_hooks[HOOK_TYPE_FD]; ptr_hook; ptr_hook = ptr_hook->next_hook) { @@ -1315,40 +1368,29 @@ hook_fd_set (fd_set *read_fds, fd_set *write_fds, fd_set *exception_fds) } else { + if (num_fd > hook_fd_pollfd_count) + break; + + hook_fd_pollfd[num_fd].fd = HOOK_FD(ptr_hook, fd); + hook_fd_pollfd[num_fd].events = 0; + hook_fd_pollfd[num_fd].revents = 0; if (HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_READ) - { - FD_SET (HOOK_FD(ptr_hook, fd), read_fds); - if (HOOK_FD(ptr_hook, fd) > max_fd) - max_fd = HOOK_FD(ptr_hook, fd); - } + hook_fd_pollfd[num_fd].events |= POLLIN; if (HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_WRITE) - { - FD_SET (HOOK_FD(ptr_hook, fd), write_fds); - if (HOOK_FD(ptr_hook, fd) > max_fd) - max_fd = HOOK_FD(ptr_hook, fd); - } - if (HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_EXCEPTION) - { - FD_SET (HOOK_FD(ptr_hook, fd), exception_fds); - if (HOOK_FD(ptr_hook, fd) > max_fd) - max_fd = HOOK_FD(ptr_hook, fd); - } + hook_fd_pollfd[num_fd].events |= POLLOUT; + + num_fd++; } } } - return max_fd; -} - -/* - * Executes fd callbacks with sets. - */ - -void -hook_fd_exec (fd_set *read_fds, fd_set *write_fds, fd_set *exception_fds) -{ - struct t_hook *ptr_hook, *next_hook; + /* perform the poll() */ + timeout = hook_timer_get_time_to_next (); + ready = poll (hook_fd_pollfd, num_fd, timeout); + if (ready <= 0) + return; + /* execute callbacks for file descriptors with activity */ hook_exec_start (); ptr_hook = weechat_hooks[HOOK_TYPE_FD]; @@ -1357,18 +1399,25 @@ hook_fd_exec (fd_set *read_fds, fd_set *write_fds, fd_set *exception_fds) next_hook = ptr_hook->next_hook; if (!ptr_hook->deleted - && !ptr_hook->running - && (((HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_READ) - && (FD_ISSET(HOOK_FD(ptr_hook, fd), read_fds))) - || ((HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_WRITE) - && (FD_ISSET(HOOK_FD(ptr_hook, fd), write_fds))) - || ((HOOK_FD(ptr_hook, flags) & HOOK_FD_FLAG_EXCEPTION) - && (FD_ISSET(HOOK_FD(ptr_hook, fd), exception_fds))))) + && !ptr_hook->running) { - ptr_hook->running = 1; - (void) (HOOK_FD(ptr_hook, callback)) (ptr_hook->callback_data, - HOOK_FD(ptr_hook, fd)); - ptr_hook->running = 0; + found = 0; + for (i = 0; i < num_fd; i++) + { + if (hook_fd_pollfd[i].fd == HOOK_FD(ptr_hook, fd) + && hook_fd_pollfd[i].revents) + { + found = 1; + break; + } + } + if (found) + { + ptr_hook->running = 1; + (void) (HOOK_FD(ptr_hook, callback)) (ptr_hook->callback_data, + HOOK_FD(ptr_hook, fd)); + ptr_hook->running = 0; + } } ptr_hook = next_hook; @@ -1784,9 +1833,8 @@ hook_process_child_read_stderr_cb (void *arg_hook_process, int fd) void hook_process_child_read_until_eof (struct t_hook *hook_process) { - struct timeval tv_timeout; - fd_set read_fds, write_fds, except_fds; - int count, fd_stdout, fd_stderr, max_fd, ready; + struct pollfd poll_fd[2]; + int count, fd_stdout, fd_stderr, num_fd, ready, i; fd_stdout = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]); fd_stderr = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]); @@ -1798,52 +1846,53 @@ hook_process_child_read_until_eof (struct t_hook *hook_process) count = 0; while (count < 1024) { - FD_ZERO (&read_fds); - FD_ZERO (&write_fds); - FD_ZERO (&except_fds); + num_fd = 0; - max_fd = -1; if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]) && ((fcntl (fd_stdout, F_GETFD) != -1) || (errno != EBADF))) { - FD_SET (fd_stdout, &read_fds); - if (fd_stdout > max_fd) - max_fd = fd_stdout; + poll_fd[num_fd].fd = fd_stdout; + poll_fd[num_fd].events = POLLIN; + poll_fd[num_fd].revents = 0; + num_fd++; } + if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]) && ((fcntl (fd_stderr, F_GETFD) != -1) || (errno != EBADF))) { - FD_SET (fd_stderr, &read_fds); - if (fd_stderr > max_fd) - max_fd = fd_stderr; + poll_fd[num_fd].fd = fd_stderr; + poll_fd[num_fd].events = POLLIN; + poll_fd[num_fd].revents = 0; + num_fd++; } - if (max_fd < 0) + if (num_fd == 0) break; - tv_timeout.tv_sec = 0; - tv_timeout.tv_usec = 0; - - ready = select (max_fd + 1, - &read_fds, &write_fds, &except_fds, - &tv_timeout); + ready = poll (poll_fd, num_fd, 0); if (ready <= 0) break; - if (FD_ISSET(fd_stdout, &read_fds)) - { - (void) hook_process_child_read_stdout_cb ( - hook_process, - HOOK_PROCESS(hook_process, - child_read[HOOK_PROCESS_STDOUT])); - } - if (FD_ISSET(fd_stderr, &read_fds)) + for (i = 0; i < num_fd; i++) { - (void) hook_process_child_read_stderr_cb ( - hook_process, - HOOK_PROCESS(hook_process, - child_read[HOOK_PROCESS_STDERR])); + if (poll_fd[i].revents & POLLIN) + { + if (poll_fd[i].fd == fd_stdout) + { + (void) hook_process_child_read_stdout_cb ( + hook_process, + HOOK_PROCESS(hook_process, + child_read[HOOK_PROCESS_STDOUT])); + } + else + { + (void) hook_process_child_read_stderr_cb ( + hook_process, + HOOK_PROCESS(hook_process, + child_read[HOOK_PROCESS_STDERR])); + } + } } count++; diff --git a/src/core/wee-hook.h b/src/core/wee-hook.h index f7ab9de62..82de5b7bd 100644 --- a/src/core/wee-hook.h +++ b/src/core/wee-hook.h @@ -467,17 +467,13 @@ extern struct t_hook *hook_timer (struct t_weechat_plugin *plugin, int max_calls, t_hook_callback_timer *callback, void *callback_data); -extern void hook_timer_time_to_next (struct timeval *tv_timeout); extern void hook_timer_exec (); extern struct t_hook *hook_fd (struct t_weechat_plugin *plugin, int fd, int flag_read, int flag_write, int flag_exception, t_hook_callback_fd *callback, void *callback_data); -extern int hook_fd_set (fd_set *read_fds, fd_set *write_fds, - fd_set *exception_fds); -extern void hook_fd_exec (fd_set *read_fds, fd_set *write_fds, - fd_set *exception_fds); +extern void hook_fd_exec (); extern struct t_hook *hook_process (struct t_weechat_plugin *plugin, const char *command, int timeout, diff --git a/src/core/wee-network.c b/src/core/wee-network.c index 92aa021ab..7eb94ca49 100644 --- a/src/core/wee-network.c +++ b/src/core/wee-network.c @@ -39,7 +39,7 @@ #include <string.h> #include <sys/types.h> #include <sys/socket.h> -#include <sys/select.h> +#include <poll.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> @@ -611,7 +611,7 @@ network_pass_proxy (const char *proxy, int sock, const char *address, int port) int network_connect (int sock, const struct sockaddr *addr, socklen_t addrlen) { - fd_set write_fds; + struct pollfd poll_fd; int ready, value; socklen_t len; @@ -628,9 +628,12 @@ network_connect (int sock, const struct sockaddr *addr, socklen_t addrlen) */ while (1) { - FD_ZERO (&write_fds); - FD_SET (sock, &write_fds); - ready = select (sock + 1, NULL, &write_fds, NULL, NULL); + poll_fd.fd = sock; + poll_fd.events = POLLOUT; + poll_fd.revents = 0; + ready = poll (&poll_fd, 1, -1); + if (ready < 0) + break; if (ready > 0) { len = sizeof (value); diff --git a/src/gui/curses/gui-curses-main.c b/src/gui/curses/gui-curses-main.c index f83d8324a..090c1ef04 100644 --- a/src/gui/curses/gui-curses-main.c +++ b/src/gui/curses/gui-curses-main.c @@ -29,7 +29,6 @@ #include <string.h> #include <signal.h> #include <time.h> -#include <sys/select.h> #include "../../core/weechat.h" #include "../../core/wee-command.h" @@ -383,10 +382,6 @@ void gui_main_loop () { struct t_hook *hook_fd_keyboard; - struct timeval tv_timeout; - fd_set read_fds, write_fds, except_fds; - int max_fd; - int ready; /* catch SIGWINCH signal: redraw screen */ util_catch_signal (SIGWINCH, &gui_main_signal_sigwinch); @@ -399,7 +394,7 @@ gui_main_loop () while (!weechat_quit) { - /* execute hook timers */ + /* execute timer hooks */ hook_timer_exec (); /* auto reset of color pairs */ @@ -424,18 +419,8 @@ gui_main_loop () gui_color_pairs_auto_reset_pending = 0; - /* wait for keyboard or network activity */ - FD_ZERO (&read_fds); - FD_ZERO (&write_fds); - FD_ZERO (&except_fds); - max_fd = hook_fd_set (&read_fds, &write_fds, &except_fds); - hook_timer_time_to_next (&tv_timeout); - ready = select (max_fd + 1, &read_fds, &write_fds, &except_fds, - &tv_timeout); - if (ready > 0) - { - hook_fd_exec (&read_fds, &write_fds, &except_fds); - } + /* execute fd hooks */ + hook_fd_exec (); } /* remove keyboard hook */ |