Package: libuim8
Version: 1:1.8.6-8
Severity: normal
Dear Maintainer,
When uim client exec() to long-running process (e.g. iceweasel
restarts), it leaks open socket to uim-helper-server, and by then
uim-helper-server begins to constantly consume memory, as it cannot
write to those sockets and have to infinitely buffer unsent messages to
them:
... strace -p `pidof uim-helper-server`|egrep '^(select|mremap)' ...
select(11, [0 1 3 4 5 6 7 9 10], [1], NULL, NULL) = 1 (in [0])
select(11, [0 1 3 4 5 6 7 9 10], [1 4 5 6 7 9 10], NULL, NULL) = 6 (out
[4 5 6 7 9 10])
note: here fd 1 points to unix-domain-socket to such iceweasel
process, and it never returns "writeable" status; there are another
socket to same iceweasel process {which was opened after restart},
which is properly handled
select(11, [0 1 3 4 5 6 7 9 10], [1], NULL, NULL) = 1 (in [0])
select(11, [0 1 3 4 5 6 7 9 10], [1 4 5 6 7 9 10], NULL, NULL) = 6 (out
[4 5 6 7 9 10])
select(11, [0 1 3 4 5 6 7 9 10], [1], NULL, NULL) = 1 (in [0])
select(11, [0 1 3 4 5 6 7 9 10], [1], NULL, NULL) = 1 (in [0])
mremap(0xb7318000, 1404928, 1409024, MREMAP_MAYMOVE) = 0xb7318000
note: this is apparently reallocation of buffered messages for this
socket; after few days, it already grown to 1.4M
select(11, [0 1 3 4 5 6 7 9 10], [1 4 5 6 7 9 10], NULL, NULL) = 6 (out
[4 5 6 7 9 10])
select(11, [0 1 3 4 5 6 7 9 10], [1], NULL, NULL) = 1 (in [0])
select(11, [0 1 3 4 5 6 7 9 10], [1 4 5 6 7 9 10], NULL, NULL) = 6 (out
[4 5 6 7 9 10])
select(11, [0 1 3 4 5 6 7 9 10], [1], NULL, NULL) = 1 (in [0])
...
Attached patch fixes this by setting CLOEXEC flag on all sockets (only
uim-helper-client.c part is definitely needed and safe; uim/socket.c
part theoretically can break scm scripts that intentionally passes open
socket fd to spawned processes [but AFAIK none of them does]).
Optional 2nd patch tries to use SOCK_CLOEXEC option, available in
linux-2.6.27+ (it would avoid race window between socket() and fcntl(),
if another thread spawned process at the exact same time with socket
creation), with fallback for older kernel.
This bug is also present in (all) older uim versions, and in the
upstream master branch as well.
P.S. @upstream: also, uim-helper-server could be improved a bit for
better handling of "stuck client" scenario [aside from client bugs {e.g.
uim-input-pad-ja is broken this way}, that could be stopped processes
(^Z/kill -STOP), etc]. E.g. it could forget/merge older prop_update &
focus_* messages [after reaching some size/last-writeable-time
threshold, to avoid complication in common-case scenario].
P.P.S. BTW, uim-helper-client.c looks *extremely* fragile. If someone,
somehow will call uim_helper_init_client twice, all hell will broke loose.
-- System Information:
Debian Release: 8.0
APT prefers stable-updates
APT policy: (500, 'stable-updates'), (500, 'stable')
Architecture: i386 (i686)
Kernel: Linux 3.16.0-4-686-pae (SMP w/1 CPU core)
Locale: LANG=ru_RU.UTF-8, LC_CTYPE=ru_RU.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
Versions of packages libuim8 depends on:
ii libc6 2.19-18
ii libgcroots0 0.8.5-4.1
ii libuim-scm0 1:1.8.6-8
ii multiarch-support 2.19-18
ii uim-common 1:1.8.6-8
libuim8 recommends no packages.
libuim8 suggests no packages.
-- no debconf information
Index: uim-1.8.6/uim/uim-helper-client.c
===================================================================
--- uim-1.8.6.orig/uim/uim-helper-client.c
+++ uim-1.8.6/uim/uim-helper-client.c
@@ -44,6 +44,7 @@
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <fcntl.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
@@ -93,6 +94,7 @@ int uim_helper_init_client_fd(void (*dis
perror("fail to create socket");
goto error;
}
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
#ifdef LOCAL_CREDS /* for NetBSD */
/* Set the socket to receive credentials on the next message */
Index: uim-1.8.6/uim/socket.c
===================================================================
--- uim-1.8.6.orig/uim/socket.c
+++ uim-1.8.6/uim/socket.c
@@ -278,7 +278,10 @@ c_freeaddrinfo(uim_lisp addrinfo_)
static uim_lisp
c_socket(uim_lisp domain_, uim_lisp type_, uim_lisp protocol_)
{
- return MAKE_INT(socket(C_INT(domain_), C_INT(type_), C_INT(protocol_)));
+ int fd = socket(C_INT(domain_), C_INT(type_), C_INT(protocol_));
+ if (fd != -1)
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+ return MAKE_INT(fd);
}
static uim_lisp
Index: uim-1.8.6/uim/uim-helper-client.c
===================================================================
diff -u uim-1.8.6.orig/uim/uim-helper-client.c uim-1.8.6/uim/uim-helper-client.c
--- uim-1.8.6.orig/uim/uim-helper-client.c
+++ uim-1.8.6/uim/uim-helper-client.c
@@ -89,6 +89,12 @@
server.sun_family = PF_UNIX;
strlcpy(server.sun_path, path, sizeof(server.sun_path));
+#ifdef SOCK_CLOEXEC
+ /* linux-2.6.27+ variant that prevents racing on concurrent fork & exec in other thread */
+ fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd == -1 && errno == EINVAL)
+ /* fallback to plain SOCK_TYPE on older kernel */
+#endif
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
perror("fail to create socket");
Index: uim-1.8.6/uim/socket.c
===================================================================
diff -u uim-1.8.6.orig/uim/socket.c uim-1.8.6/uim/socket.c
--- uim-1.8.6.orig/uim/socket.c
+++ uim-1.8.6/uim/socket.c
@@ -278,7 +278,15 @@
static uim_lisp
c_socket(uim_lisp domain_, uim_lisp type_, uim_lisp protocol_)
{
- int fd = socket(C_INT(domain_), C_INT(type_), C_INT(protocol_));
+ int type_i = C_INT(type_);
+ int fd;
+#ifdef SOCK_CLOEXEC
+ /* linux-2.6.27+ variant that prevents racing on concurrent fork & exec in other thread */
+ fd = socket(C_INT(domain_), type_i | SOCK_CLOEXEC, C_INT(protocol_));
+ if (fd == -1 && errno == EINVAL)
+ /* fallback to plain SOCK_TYPE on older kernel */
+#endif
+ fd = socket(C_INT(domain_), type_i, C_INT(protocol_));
if (fd != -1)
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
return MAKE_INT(fd);