From 40ff6d7e8dceca227e7f8a3e8e0d58b2c66d19b4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 2 Dec 2009 12:24:42 +0100 Subject: [PATCH] Don't leak file descriptors We're leaking file descriptors to child processes. Set FD_CLOEXEC on file descriptors that don't need to be passed to children to stop this misbehaviour. Signed-off-by: Kevin Wolf Signed-off-by: Anthony Liguori --- block/raw-posix.c | 2 +- configure | 20 +++++++++ gdbstub.c | 6 +++ kvm-all.c | 2 +- migration-tcp.c | 6 +-- migration-unix.c | 6 +-- net/socket.c | 8 ++-- osdep.c | 104 ++++++++++++++++++++++++++++++++++++++++++++- posix-aio-compat.c | 2 +- qemu-char.c | 8 ++-- qemu-common.h | 7 +++ qemu-sockets.c | 10 ++--- qemu_socket.h | 2 + slirp/misc.c | 4 +- slirp/slirp.h | 4 ++ slirp/socket.c | 2 +- slirp/tcp_subr.c | 2 +- slirp/udp.c | 4 +- vl.c | 11 +++-- vnc.c | 2 +- 20 files changed, 178 insertions(+), 34 deletions(-) diff --git a/block/raw-posix.c b/block/raw-posix.c index 706799fa3..5a6a22bee 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -153,7 +153,7 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, s->open_flags |= O_DSYNC; s->fd = -1; - fd = open(filename, s->open_flags, 0644); + fd = qemu_open(filename, s->open_flags, 0644); if (fd < 0) { ret = -errno; if (ret == -EROFS) diff --git a/configure b/configure index 8b8b94ce0..ef15948d7 100755 --- a/configure +++ b/configure @@ -1570,6 +1570,23 @@ if compile_prog "" "" ; then pipe2=yes fi +# check if accept4 is there +accept4=no +cat > $TMPC << EOF +#define _GNU_SOURCE +#include +#include + +int main(void) +{ + accept4(0, NULL, NULL, SOCK_CLOEXEC); + return 0; +} +EOF +if compile_prog "" "" ; then + accept4=yes +fi + # check if tee/splice is there. vmsplice was added same time. splice=no cat > $TMPC << EOF @@ -2014,6 +2031,9 @@ fi if test "$pipe2" = "yes" ; then echo "CONFIG_PIPE2=y" >> $config_host_mak fi +if test "$accept4" = "yes" ; then + echo "CONFIG_ACCEPT4=y" >> $config_host_mak +fi if test "$splice" = "yes" ; then echo "CONFIG_SPLICE=y" >> $config_host_mak fi diff --git a/gdbstub.c b/gdbstub.c index 055093f89..5a4f7d49b 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2356,6 +2356,9 @@ static void gdb_accept(void) perror("accept"); return; } else if (fd >= 0) { +#ifndef _WIN32 + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif break; } } @@ -2385,6 +2388,9 @@ static int gdbserver_open(int port) perror("socket"); return -1; } +#ifndef _WIN32 + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif /* allow fast reuse */ val = 1; diff --git a/kvm-all.c b/kvm-all.c index b605caaef..b8ffe546c 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -416,7 +416,7 @@ int kvm_init(int smp_cpus) s->slots[i].slot = i; s->vmfd = -1; - s->fd = open("/dev/kvm", O_RDWR); + s->fd = qemu_open("/dev/kvm", O_RDWR); if (s->fd == -1) { fprintf(stderr, "Could not access KVM kernel module: %m\n"); ret = -errno; diff --git a/migration-tcp.c b/migration-tcp.c index b77ed8762..2cfa8cba5 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -105,7 +105,7 @@ MigrationState *tcp_start_outgoing_migration(Monitor *mon, s->state = MIG_STATE_ACTIVE; s->mon = NULL; s->bandwidth_limit = bandwidth_limit; - s->fd = socket(PF_INET, SOCK_STREAM, 0); + s->fd = qemu_socket(PF_INET, SOCK_STREAM, 0); if (s->fd == -1) { qemu_free(s); return NULL; @@ -146,7 +146,7 @@ static void tcp_accept_incoming_migration(void *opaque) int c, ret; do { - c = accept(s, (struct sockaddr *)&addr, &addrlen); + c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); } while (c == -1 && socket_error() == EINTR); dprintf("accepted migration\n"); @@ -193,7 +193,7 @@ int tcp_start_incoming_migration(const char *host_port) return -EINVAL; } - s = socket(PF_INET, SOCK_STREAM, 0); + s = qemu_socket(PF_INET, SOCK_STREAM, 0); if (s == -1) return -socket_error(); diff --git a/migration-unix.c b/migration-unix.c index 7dd787cd9..783228b3b 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -104,7 +104,7 @@ MigrationState *unix_start_outgoing_migration(Monitor *mon, s->state = MIG_STATE_ACTIVE; s->mon = NULL; s->bandwidth_limit = bandwidth_limit; - s->fd = socket(PF_UNIX, SOCK_STREAM, 0); + s->fd = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (s->fd < 0) { dprintf("Unable to open socket"); goto err_after_alloc; @@ -150,7 +150,7 @@ static void unix_accept_incoming_migration(void *opaque) int c, ret; do { - c = accept(s, (struct sockaddr *)&addr, &addrlen); + c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); } while (c == -1 && socket_error() == EINTR); dprintf("accepted migration\n"); @@ -191,7 +191,7 @@ int unix_start_incoming_migration(const char *path) dprintf("Attempting to start an incoming migration\n"); - sock = socket(PF_UNIX, SOCK_STREAM, 0); + sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { fprintf(stderr, "Could not open unix socket: %s\n", strerror(errno)); return -EINVAL; diff --git a/net/socket.c b/net/socket.c index 7331d87c6..5533737e4 100644 --- a/net/socket.c +++ b/net/socket.c @@ -161,7 +161,7 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) return -1; } - fd = socket(PF_INET, SOCK_DGRAM, 0); + fd = qemu_socket(PF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("socket(PF_INET, SOCK_DGRAM)"); return -1; @@ -355,7 +355,7 @@ static void net_socket_accept(void *opaque) for(;;) { len = sizeof(saddr); - fd = accept(s->fd, (struct sockaddr *)&saddr, &len); + fd = qemu_accept(s->fd, (struct sockaddr *)&saddr, &len); if (fd < 0 && errno != EINTR) { return; } else if (fd >= 0) { @@ -386,7 +386,7 @@ static int net_socket_listen_init(VLANState *vlan, s = qemu_mallocz(sizeof(NetSocketListenState)); - fd = socket(PF_INET, SOCK_STREAM, 0); + fd = qemu_socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; @@ -427,7 +427,7 @@ static int net_socket_connect_init(VLANState *vlan, if (parse_host_port(&saddr, host_str) < 0) return -1; - fd = socket(PF_INET, SOCK_STREAM, 0); + fd = qemu_socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; diff --git a/osdep.c b/osdep.c index fd8bbd7e2..7509c5b22 100644 --- a/osdep.c +++ b/osdep.c @@ -121,7 +121,7 @@ int qemu_create_pidfile(const char *filename) #ifndef _WIN32 int fd; - fd = open(filename, O_RDWR | O_CREAT, 0600); + fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); if (fd == -1) return -1; @@ -201,11 +201,113 @@ int inet_aton(const char *cp, struct in_addr *ia) ia->s_addr = addr; return 1; } + +void qemu_set_cloexec(int fd) +{ +} + #else + void socket_set_nonblock(int fd) { int f; f = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, f | O_NONBLOCK); } + +void qemu_set_cloexec(int fd) +{ + int f; + f = fcntl(fd, F_GETFD); + fcntl(fd, F_SETFD, f | FD_CLOEXEC); +} + +#endif + +/* + * Opens a file with FD_CLOEXEC set + */ +int qemu_open(const char *name, int flags, ...) +{ + int ret; + int mode = 0; + + if (flags & O_CREAT) { + va_list ap; + + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + } + +#ifdef O_CLOEXEC + ret = open(name, flags | O_CLOEXEC, mode); +#else + ret = open(name, flags, mode); + if (ret >= 0) { + qemu_set_cloexec(ret); + } #endif + + return ret; +} + +#ifndef _WIN32 +/* + * Creates a pipe with FD_CLOEXEC set on both file descriptors + */ +int qemu_pipe(int pipefd[2]) +{ + int ret; + +#ifdef CONFIG_PIPE2 + ret = pipe2(pipefd, O_CLOEXEC); +#else + ret = pipe(pipefd); + if (ret == 0) { + qemu_set_cloexec(pipefd[0]); + qemu_set_cloexec(pipefd[1]); + } +#endif + + return ret; +} +#endif + +/* + * Opens a socket with FD_CLOEXEC set + */ +int qemu_socket(int domain, int type, int protocol) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socket(domain, type | SOCK_CLOEXEC, protocol); +#else + ret = socket(domain, type, protocol); + if (ret >= 0) { + qemu_set_cloexec(ret); + } +#endif + + return ret; +} + +/* + * Accept a connection and set FD_CLOEXEC + */ +int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + int ret; + +#ifdef CONFIG_ACCEPT4 + ret = accept4(s, addr, addrlen, SOCK_CLOEXEC); +#else + ret = accept(s, addr, addrlen); + if (ret >= 0) { + qemu_set_cloexec(ret); + } +#endif + + return ret; +} diff --git a/posix-aio-compat.c b/posix-aio-compat.c index 7f391c937..ac247e1e3 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -625,7 +625,7 @@ int paio_init(void) sigaction(SIGUSR2, &act, NULL); s->first_aio = NULL; - if (pipe(fds) == -1) { + if (qemu_pipe(fds) == -1) { fprintf(stderr, "failed to create pipe\n"); return -1; } diff --git a/qemu-char.c b/qemu-char.c index e202585fd..da5c15c4f 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -620,7 +620,7 @@ static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) { int fd_out; - TFR(fd_out = open(qemu_opt_get(opts, "path"), + TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); if (fd_out < 0) return NULL; @@ -640,8 +640,8 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) snprintf(filename_in, 256, "%s.in", filename); snprintf(filename_out, 256, "%s.out", filename); - TFR(fd_in = open(filename_in, O_RDWR | O_BINARY)); - TFR(fd_out = open(filename_out, O_RDWR | O_BINARY)); + TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY)); + TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY)); if (fd_in < 0 || fd_out < 0) { if (fd_in >= 0) close(fd_in); @@ -2101,7 +2101,7 @@ static void tcp_chr_accept(void *opaque) len = sizeof(saddr); addr = (struct sockaddr *)&saddr; } - fd = accept(s->listen_fd, addr, &len); + fd = qemu_accept(s->listen_fd, addr, &len); if (fd < 0 && errno != EINTR) { return; } else if (fd >= 0) { diff --git a/qemu-common.h b/qemu-common.h index 57af677f0..8630f8c41 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -159,6 +159,13 @@ void *get_mmap_addr(unsigned long size); void qemu_mutex_lock_iothread(void); void qemu_mutex_unlock_iothread(void); +int qemu_open(const char *name, int flags, ...); +void qemu_set_cloexec(int fd); + +#ifndef _WIN32 +int qemu_pipe(int pipefd[2]); +#endif + /* Error handling. */ void QEMU_NORETURN hw_error(const char *fmt, ...) diff --git a/qemu-sockets.c b/qemu-sockets.c index 8801453cb..8850516f2 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -160,7 +160,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, uaddr,INET6_ADDRSTRLEN,uport,32, NI_NUMERICHOST | NI_NUMERICSERV); - slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol); + slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol); if (slisten < 0) { fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__, inet_strfamily(e->ai_family), strerror(errno)); @@ -258,7 +258,7 @@ int inet_connect_opts(QemuOpts *opts) fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__); continue; } - sock = socket(e->ai_family, e->ai_socktype, e->ai_protocol); + sock = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol); if (sock < 0) { fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__, inet_strfamily(e->ai_family), strerror(errno)); @@ -351,7 +351,7 @@ int inet_dgram_opts(QemuOpts *opts) } /* create socket */ - sock = socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol); + sock = qemu_socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol); if (sock < 0) { fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__, inet_strfamily(peer->ai_family), strerror(errno)); @@ -505,7 +505,7 @@ int unix_listen_opts(QemuOpts *opts) const char *path = qemu_opt_get(opts, "path"); int sock, fd; - sock = socket(PF_UNIX, SOCK_STREAM, 0); + sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket(unix)"); return -1; @@ -560,7 +560,7 @@ int unix_connect_opts(QemuOpts *opts) return -1; } - sock = socket(PF_UNIX, SOCK_STREAM, 0); + sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket(unix)"); return -1; diff --git a/qemu_socket.h b/qemu_socket.h index c253b3241..86bdbf53d 100644 --- a/qemu_socket.h +++ b/qemu_socket.h @@ -32,6 +32,8 @@ int inet_aton(const char *cp, struct in_addr *ia); #include "qemu-option.h" /* misc helpers */ +int qemu_socket(int domain, int type, int protocol); +int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); void socket_set_nonblock(int fd); int send_all(int fd, const void *buf, int len1); diff --git a/slirp/misc.c b/slirp/misc.c index e9f08fdac..c76ad8fef 100644 --- a/slirp/misc.c +++ b/slirp/misc.c @@ -132,7 +132,7 @@ fork_exec(struct socket *so, const char *ex, int do_pty) addr.sin_port = 0; addr.sin_addr.s_addr = INADDR_ANY; - if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0 || + if ((s = qemu_socket(AF_INET, SOCK_STREAM, 0)) < 0 || bind(s, (struct sockaddr *)&addr, addrlen) < 0 || listen(s, 1) < 0) { lprint("Error: inet socket: %s\n", strerror(errno)); @@ -165,7 +165,7 @@ fork_exec(struct socket *so, const char *ex, int do_pty) * Connect to the socket * XXX If any of these fail, we're in trouble! */ - s = socket(AF_INET, SOCK_STREAM, 0); + s = qemu_socket(AF_INET, SOCK_STREAM, 0); addr.sin_addr = loopback_addr; do { ret = connect(s, (struct sockaddr *)&addr, addrlen); diff --git a/slirp/slirp.h b/slirp/slirp.h index 9ef57ea39..98a26442a 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -197,6 +197,10 @@ int inet_aton(const char *cp, struct in_addr *ia); #include "bootp.h" #include "tftp.h" +/* osdep.c */ +int qemu_socket(int domain, int type, int protocol); + + struct Slirp { QTAILQ_ENTRY(Slirp) entry; diff --git a/slirp/socket.c b/slirp/socket.c index 207109c7e..cf6e6a957 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -626,7 +626,7 @@ tcp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr, addr.sin_addr.s_addr = haddr; addr.sin_port = hport; - if (((s = socket(AF_INET,SOCK_STREAM,0)) < 0) || + if (((s = qemu_socket(AF_INET,SOCK_STREAM,0)) < 0) || (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) || (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) || (listen(s,1) < 0)) { diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index 04173450d..7851307fa 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -325,7 +325,7 @@ int tcp_fconnect(struct socket *so) DEBUG_CALL("tcp_fconnect"); DEBUG_ARG("so = %lx", (long )so); - if( (ret=so->s=socket(AF_INET,SOCK_STREAM,0)) >= 0) { + if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) { int opt, s=so->s; struct sockaddr_in addr; diff --git a/slirp/udp.c b/slirp/udp.c index a88b645cc..d6c39b97b 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -302,7 +302,7 @@ int udp_output(struct socket *so, struct mbuf *m, int udp_attach(struct socket *so) { - if((so->s = socket(AF_INET,SOCK_DGRAM,0)) != -1) { + if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) { so->so_expire = curtime + SO_EXPIRE; insque(so, &so->slirp->udb); } @@ -350,7 +350,7 @@ udp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr, if (!so) { return NULL; } - so->s = socket(AF_INET,SOCK_DGRAM,0); + so->s = qemu_socket(AF_INET,SOCK_DGRAM,0); so->so_expire = curtime + SO_EXPIRE; insque(so, &slirp->udb); diff --git a/vl.c b/vl.c index 0816ed701..d6f196cff 100644 --- a/vl.c +++ b/vl.c @@ -1243,7 +1243,7 @@ static int hpet_start_timer(struct qemu_alarm_timer *t) struct hpet_info info; int r, fd; - fd = open("/dev/hpet", O_RDONLY); + fd = qemu_open("/dev/hpet", O_RDONLY); if (fd < 0) return -1; @@ -1292,7 +1292,7 @@ static int rtc_start_timer(struct qemu_alarm_timer *t) int rtc_fd; unsigned long current_rtc_freq = 0; - TFR(rtc_fd = open("/dev/rtc", O_RDONLY)); + TFR(rtc_fd = qemu_open("/dev/rtc", O_RDONLY)); if (rtc_fd < 0) return -1; ioctl(rtc_fd, RTC_IRQP_READ, ¤t_rtc_freq); @@ -3337,7 +3337,7 @@ static int qemu_event_init(void) int err; int fds[2]; - err = pipe(fds); + err = qemu_pipe(fds); if (err == -1) return -errno; @@ -5507,6 +5507,9 @@ int main(int argc, char **argv, char **envp) } else if (pid < 0) exit(1); + close(fds[0]); + qemu_set_cloexec(fds[1]); + setsid(); pid = fork(); @@ -5907,7 +5910,7 @@ int main(int argc, char **argv, char **envp) exit(1); chdir("/"); - TFR(fd = open("/dev/null", O_RDWR)); + TFR(fd = qemu_open("/dev/null", O_RDWR)); if (fd == -1) exit(1); } diff --git a/vnc.c b/vnc.c index 2bb802469..32c467880 100644 --- a/vnc.c +++ b/vnc.c @@ -2247,7 +2247,7 @@ static void vnc_listen_read(void *opaque) /* Catch-up */ vga_hw_update(); - int csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); + int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { vnc_connect(vs, csock); } -- 2.39.2