[Dovecot] File descriptor leak in sieve-extprograms
I've tried to use sieve-filter for my inbox (~6000 messages). It failed after some work with this backtrace: sieve-filter(kas): Error: socketpair() failed: Too many open files sieve-filter(kas): Panic: file script-client-local.c: line 155 (script_client_local_disconnect): assertion failed: (pid >= 0) sieve-filter(kas): Error: Raw backtrace: /usr/lib/dovecot/libdovecot.so.0(+0x66a71) [0x7f020e717a71] -> /usr/lib/dovecot/libdovecot.so.0(default_fatal_handler+0x2a) [0x7f020e717ada] -> /usr/lib/dovecot/libdovecot.so.0(i_fatal+0) [0x7f020e6d32ee] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x4baf) [0x7f020dae0baf] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x58ca) [0x7f020dae18ca] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(script_client_fail+0x2f) [0x7f020dae199f] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(script_client_run+0xc4) [0x7f020dae1fd4] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x6134) [0x7f020dae2134] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_result_execute+0x3ac) [0x7f020ecd1adc] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_execute+0x4a) [0x7f020ece05aa] -> /usr/bin/sieve-filter(main+0x6a9) [0x403b49] -> /usr/lib/libc.so.6(__libc_start_ma in+0xf5) [0x7f020e327bc5] -> /usr/bin/sieve-filter() [0x404111] The reason is file descriptor leak in "script client local". script_client_local_close_output() shutdowns write side of descriptor and sets sclient->fd_out to -1, but never closes the descriptor. I've tried to fix this by the patch below. Tests works fine, but sieve-filter crashed in other way: sieve-filter(kas): Panic: epoll_ctl(del, 13) failed: Bad file descriptor sieve-filter(kas): Error: Raw backtrace: /usr/lib/dovecot/libdovecot.so.0(+0x66a71) [0x7f0ed406fa71] -> /usr/lib/dovecot/libdovecot.so.0(default_fatal_handler+0x2a) [0x7f0ed406fada] -> /usr/lib/dovecot/libdovecot.so.0(i_fatal+0) [0x7f0ed402b2ee] -> /usr/lib/dovecot/libdovecot.so.0(+0x776f5) [0x7f0ed40806f5] -> /usr/lib/dovecot/libdovecot.so.0(+0x8009a) [0x7f0ed408909a] -> /usr/lib/dovecot/libdovecot.so.0(io_stream_unref+0x25) [0x7f0ed40764c5] -> /usr/lib/dovecot/libdovecot.so.0(o_stream_unref+0x26) [0x7f0ed4088136] -> /usr/lib/dovecot/libdovecot.so.0(+0x80e48) [0x7f0ed4089e48] -> /usr/lib/dovecot/libdovecot.so.0(io_loop_call_io+0x27) [0x7f0ed407fa47] -> /usr/lib/dovecot/libdovecot.so.0(io_loop_handler_run+0xd7) [0x7f0ed40807d7] -> /usr/lib/dovecot/libdovecot.so.0(io_loop_run+0x38) [0x7f0ed407f5e8] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(script_client_run+0x9c) [0x7f0ed343af8c] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x6114) [0x 7f0ed343b114] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_result_execute+0x3ac) [0x7f0ed4629adc] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_execute+0x4a) [0x7f0ed46385aa] -> /usr/bin/sieve-filter(main+0x6a9) [0x403b49] -> /usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7f0ed3c7fbc5] -> /usr/bin/sieve-filter() [0x404111] I failed to unfold this in a reasonable time. Any ideas? diff --git a/src/plugins/sieve-extprograms/script-client-local.c b/src/plugins/sieve-extprograms/script-client-local.c index 56d32f707c09..f54ef82132cf 100644 --- a/src/plugins/sieve-extprograms/script-client-local.c +++ b/src/plugins/sieve-extprograms/script-client-local.c @@ -139,7 +139,6 @@ static int script_client_local_close_output(struct script_client *sclient) i_error("shutdown(%s, SHUT_WR) failed: %m", sclient->path); return -1; } - sclient->fd_out = -1; return 1; } diff --git a/src/plugins/sieve-extprograms/script-client.c b/src/plugins/sieve-extprograms/script-client.c index 06a5d5d54ba9..6fefca4c9f26 100644 --- a/src/plugins/sieve-extprograms/script-client.c +++ b/src/plugins/sieve-extprograms/script-client.c @@ -68,7 +68,8 @@ static void script_client_disconnect if (sclient->fd_in != -1 && close(sclient->fd_in) < 0) i_error("close(%s) failed: %m", sclient->path); - if (sclient->fd_out != -1 && sclient->fd_out != sclient->fd_out) + if (sclient->fd_out != -1 && sclient->fd_out != sclient->fd_in + && close(sclient->fd_out) < 0) i_error("close(%s/out) failed: %m", sclient->path); sclient->fd_in = sclient->fd_out = -1; -- Kirill A. Shutemov
On 12/13/2013 2:07 AM, Kirill A. Shutemov wrote:
I've tried to use sieve-filter for my inbox (~6000 messages). It failed after some work with this backtrace:
sieve-filter(kas): Error: socketpair() failed: Too many open files sieve-filter(kas): Panic: file script-client-local.c: line 155 (script_client_local_disconnect): assertion failed: (pid >= 0) sieve-filter(kas): Error: Raw backtrace: /usr/lib/dovecot/libdovecot.so.0(+0x66a71) [0x7f020e717a71] -> /usr/lib/dovecot/libdovecot.so.0(default_fatal_handler+0x2a) [0x7f020e717ada] -> /usr/lib/dovecot/libdovecot.so.0(i_fatal+0) [0x7f020e6d32ee] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x4baf) [0x7f020dae0baf] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x58ca) [0x7f020dae18ca] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(script_client_fail+0x2f) [0x7f020dae199f] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(script_client_run+0xc4) [0x7f020dae1fd4] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x6134) [0x7f020dae2134] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_result_execute+0x3ac) [0x7f020ecd1adc] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_execute+0x4a) [0x7f020ece05aa] -> /usr/bin/sieve-filter(main+0x6a9) [0x403b49] -> /usr/lib/libc.so.6(__libc_ start_ma in+0xf5) [0x7f020e327bc5] -> /usr/bin/sieve-filter() [0x404111]
The reason is file descriptor leak in "script client local". script_client_local_close_output() shutdowns write side of descriptor and sets sclient->fd_out to -1, but never closes the descriptor.
I've tried to fix this by the patch below. Tests works fine, but sieve-filter crashed in other way:
Wow, this is a very interesting stupidity on my part, especially the
sclient->fd_out != sclient->fd_out
.
This probably fixes it, although I am not entirely sure. I am mainly a bit puzzled on how this causes the fd leak; the fd_in and fd_out are for the current backends always the same fd.
Regards,
Stephan.
Stephan Bosch wrote:
On 12/13/2013 2:07 AM, Kirill A. Shutemov wrote:
I've tried to use sieve-filter for my inbox (~6000 messages). It failed after some work with this backtrace:
sieve-filter(kas): Error: socketpair() failed: Too many open files sieve-filter(kas): Panic: file script-client-local.c: line 155 (script_client_local_disconnect): assertion failed: (pid >= 0) sieve-filter(kas): Error: Raw backtrace: /usr/lib/dovecot/libdovecot.so.0(+0x66a71) [0x7f020e717a71] -> /usr/lib/dovecot/libdovecot.so.0(default_fatal_handler+0x2a) [0x7f020e717ada] -> /usr/lib/dovecot/libdovecot.so.0(i_fatal+0) [0x7f020e6d32ee] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x4baf) [0x7f020dae0baf] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x58ca) [0x7f020dae18ca] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(script_client_fail+0x2f) [0x7f020dae199f] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(script_client_run+0xc4) [0x7f020dae1fd4] -> /usr/lib/dovecot/modules/sieve/lib90_sieve_extprograms_plugin.so(+0x6134) [0x7f020dae2134] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_result_execute+0x3ac) [0x7f020ecd1adc] -> /usr/lib/dovecot/libdovecot-sieve.so.0(sieve_execute+0x4a) [0x7f020ece05aa] -> /usr/bin/sieve-filter(main+0x6a9) [0x403b49] -> /usr/lib/libc.so.6(__libc_ start_ma in+0xf5) [0x7f020e327bc5] -> /usr/bin/sieve-filter() [0x404111]
The reason is file descriptor leak in "script client local". script_client_local_close_output() shutdowns write side of descriptor and sets sclient->fd_out to -1, but never closes the descriptor.
I've tried to fix this by the patch below. Tests works fine, but sieve-filter crashed in other way:
Wow, this is a very interesting stupidity on my part, especially the `sclient->fd_out != sclient->fd_out`.
This probably fixes it, although I am not entirely sure. I am mainly a bit puzzled on how this causes the fd leak; the fd_in and fd_out are for the current backends always the same fd.
fd_in is not opened in my case: -1 all the way. I use vnd.dovecot.pipe. I've hackaround sieve-filter crash with the ugly patch. I don't know how to fix this properly. diff --git a/src/plugins/sieve-extprograms/script-client-local.c b/src/plugins/sieve-extprograms/script-client-local.c index 56d32f707c09..f54ef82132cf 100644 --- a/src/plugins/sieve-extprograms/script-client-local.c +++ b/src/plugins/sieve-extprograms/script-client-local.c @@ -139,7 +139,6 @@ static int script_client_local_close_output(struct script_client *sclient) i_error("shutdown(%s, SHUT_WR) failed: %m", sclient->path); return -1; } - sclient->fd_out = -1; return 1; } diff --git a/src/plugins/sieve-extprograms/script-client.c b/src/plugins/sieve-extprograms/script-client.c index 06a5d5d54ba9..b94d2029c176 100644 --- a/src/plugins/sieve-extprograms/script-client.c +++ b/src/plugins/sieve-extprograms/script-client.c @@ -47,13 +47,10 @@ static void script_client_disconnect if ( sclient->ioloop != NULL ) io_loop_stop(sclient->ioloop); - if ( sclient->disconnected ) - return; - if ( (ret=sclient->close_output(sclient)) < 0 ) error = TRUE; - - if ( (ret=sclient->disconnect(sclient, force)) < 0 ) + + if (!sclient->disconnected && (ret=sclient->disconnect(sclient, force)) < 0 ) error = TRUE; if ( sclient->script_input != NULL ) @@ -68,10 +65,13 @@ static void script_client_disconnect if (sclient->fd_in != -1 && close(sclient->fd_in) < 0) i_error("close(%s) failed: %m", sclient->path); - if (sclient->fd_out != -1 && sclient->fd_out != sclient->fd_out) - i_error("close(%s/out) failed: %m", sclient->path); - sclient->fd_in = sclient->fd_out = -1; - + sclient->fd_in = -1; + if (force && sclient->fd_out != -1 && sclient->fd_out != sclient->fd_in) { + if (close(sclient->fd_out) < 0) + i_error("close(%s/out) failed: %m", sclient->path); + sclient->fd_out = -1; + } + sclient->disconnected = TRUE; if (error && sclient->error == SCRIPT_CLIENT_ERROR_NONE ) { sclient->error = SCRIPT_CLIENT_ERROR_UNKNOWN; -- Kirill A. Shutemov
On 12/13/2013 11:14 PM, Kirill A. Shutemov wrote:
Stephan Bosch wrote:
This probably fixes it, although I am not entirely sure. I am mainly a bit puzzled on how this causes the fd leak; the fd_in and fd_out are for the current backends always the same fd. fd_in is not opened in my case: -1 all the way. I use vnd.dovecot.pipe.
Ah right, that explains it.
I've hackaround sieve-filter crash with the ugly patch. I don't know how to fix this properly.
Could you provide more details of your setup (configuration, sieve-filter command line, Sieve script), so that I can reproduce the problem at this end?
Regards,
Stephan.
Stephan Bosch wrote:
On 12/13/2013 11:14 PM, Kirill A. Shutemov wrote:
Stephan Bosch wrote:
This probably fixes it, although I am not entirely sure. I am mainly a bit puzzled on how this causes the fd leak; the fd_in and fd_out are for the current backends always the same fd. fd_in is not opened in my case: -1 all the way. I use vnd.dovecot.pipe.
Ah right, that explains it.
I've hackaround sieve-filter crash with the ugly patch. I don't know how to fix this properly.
Could you provide more details of your setup (configuration, sieve-filter command line, Sieve script), so that I can reproduce the problem at this end?
Command:
sieve-filter -e -v -W ~/.dovecot.sieve INBOX expunge
Script:
# vim: set ft=sieve :
require ["date", "variables", "fileinto", "mailbox", "vnd.dovecot.pipe"];
if date :matches :originalzone "date" "day" "*" { set "day" "${1}"; } if date :matches :originalzone "date" "month" "*" { set "month" "${1}"; } if date :matches :originalzone "date" "year" "*" { set "year" "${1}"; }
if anyof ( exists "List-id", address :matches :domain "from" "*plus.google.com", header :contains "Message-Id" "forum.ixbt.com", address :matches :domain "from" "*livejournal.com", address :matches :domain "from" "*youtube.com", address :is "to" "git-update-subscribers@altlinux.org" ) { pipe "notmuch" [ "insert", "--folder=${year}/${month}/${day}", "--create-folder", "-new", "-inbox", "+unread", "+lists" ]; } else { pipe "notmuch" [ "insert", "--folder=${year}/${month}/${day}", "--create-folder", "-new", "+inbox", "+unread" ]; }
-- Kirill A. Shutemov
On 12/14/2013 5:39 PM, Kirill A. Shutemov wrote:
Stephan Bosch wrote:
On 12/13/2013 11:14 PM, Kirill A. Shutemov wrote:
Stephan Bosch wrote:
This probably fixes it, although I am not entirely sure. I am mainly a bit puzzled on how this causes the fd leak; the fd_in and fd_out are for the current backends always the same fd. fd_in is not opened in my case: -1 all the way. I use vnd.dovecot.pipe. Ah right, that explains it.
I've hackaround sieve-filter crash with the ugly patch. I don't know how to fix this properly. Could you provide more details of your setup (configuration, sieve-filter command line, Sieve script), so that I can reproduce the problem at this end? Command:
sieve-filter -e -v -W ~/.dovecot.sieve INBOX expunge
This should fix it:
http://hg.rename-it.nl/dovecot-2.2-pigeonhole/rev/8612191c5069
I tested it here with several hundred thousand old spam e-mails.
Regards,
Stephan.
On Sat, Dec 21, 2013 at 03:18:31PM +0100, Stephan Bosch wrote:
On 12/14/2013 5:39 PM, Kirill A. Shutemov wrote:
Stephan Bosch wrote:
On 12/13/2013 11:14 PM, Kirill A. Shutemov wrote:
Stephan Bosch wrote:
This probably fixes it, although I am not entirely sure. I am mainly a bit puzzled on how this causes the fd leak; the fd_in and fd_out are for the current backends always the same fd. fd_in is not opened in my case: -1 all the way. I use vnd.dovecot.pipe. Ah right, that explains it.
I've hackaround sieve-filter crash with the ugly patch. I don't know how to fix this properly. Could you provide more details of your setup (configuration, sieve-filter command line, Sieve script), so that I can reproduce the problem at this end? Command:
sieve-filter -e -v -W ~/.dovecot.sieve INBOX expunge
This should fix it:
http://hg.rename-it.nl/dovecot-2.2-pigeonhole/rev/8612191c5069
I tested it here with several hundred thousand old spam e-mails.
Verified with valgrind too. Thanks.
-- Kirill A. Shutemov
participants (3)
-
Kirill A. Shutemov
-
Kirill A. Shutemov
-
Stephan Bosch