Add a facility for tracking open file descriptors. Information about
still open files is dumped out exit. Enabled using the --track-fds
switch.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2031 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/corecheck/tests/Makefile.am b/corecheck/tests/Makefile.am
index 89a3659..8d8ad6b 100644
--- a/corecheck/tests/Makefile.am
+++ b/corecheck/tests/Makefile.am
@@ -8,6 +8,15 @@
EXTRA_DIST = $(noinst_SCRIPTS) \
erringfds.stderr.exp erringfds.stdout.exp erringfds.vgtest \
+ fdleak_cmsg.stderr.exp fdleak_cmsg.vgtest \
+ fdleak_creat.stderr.exp fdleak_creat.vgtest \
+ fdleak_dup.stderr.exp fdleak_dup.vgtest \
+ fdleak_dup2.stderr.exp fdleak_dup2.vgtest \
+ fdleak_fcntl.stderr.exp fdleak_fcntl.vgtest \
+ fdleak_ipv4.stderr.exp fdleak_ipv4.stdout.exp fdleak_ipv4.vgtest \
+ fdleak_open.stderr.exp fdleak_open.vgtest \
+ fdleak_pipe.stderr.exp fdleak_pipe.vgtest \
+ fdleak_socketpair.stderr.exp fdleak_socketpair.vgtest \
pth_atfork1.stderr.exp pth_atfork1.stdout.exp pth_atfork1.vgtest \
pth_cancel2.stderr.exp pth_cancel2.vgtest \
pth_cvsimple.stderr.exp pth_cvsimple.stdout.exp pth_cvsimple.vgtest \
@@ -21,7 +30,9 @@
vgprintf.stderr.exp vgprintf.stdout.exp vgprintf.vgtest
check_PROGRAMS = \
- erringfds sigkill res_search \
+ erringfds fdleak_cmsg fdleak_creat fdleak_dup fdleak_dup2 \
+ fdleak_fcntl fdleak_ipv4 fdleak_open fdleak_pipe \
+ fdleak_socketpair sigkill res_search \
pth_atfork1 pth_cancel2 pth_cvsimple pth_empty \
pth_exit pth_mutexspeed pth_once \
vgprintf
@@ -31,8 +42,17 @@
# C ones
erringfds_SOURCES = erringfds.c
-sigkill_SOURCES = sigkill.c
-vgprintf_SOURCES = vgprintf.c
+fdleak_cmsg_SOURCES = fdleak_cmsg.c
+fdleak_creat_SOURCES = fdleak_creat.c
+fdleak_dup_SOURCES = fdleak_dup.c
+fdleak_dup2_SOURCES = fdleak_dup2.c
+fdleak_fcntl_SOURCES = fdleak_fcntl.c
+fdleak_ipv4_SOURCES = fdleak_ipv4.c
+fdleak_open_SOURCES = fdleak_open.c
+fdleak_pipe_SOURCES = fdleak_pipe.c
+fdleak_socketpair_SOURCES = fdleak_socketpair.c
+sigkill_SOURCES = sigkill.c
+vgprintf_SOURCES = vgprintf.c
# Pthread ones
pth_atfork1_SOURCES = pth_atfork1.c
diff --git a/corecheck/tests/fdleak_cmsg.c b/corecheck/tests/fdleak_cmsg.c
new file mode 100644
index 0000000..a6dccb1
--- /dev/null
+++ b/corecheck/tests/fdleak_cmsg.c
@@ -0,0 +1,182 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+char filea[24];
+char fileb[24];
+char sock[24];
+
+void
+server ()
+{
+ int s, fd1, fd2;
+ struct sockaddr_un addr;
+
+ fd1 = open(filea, O_RDWR | O_CREAT | O_TRUNC, 0750);
+ if(fd1 == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ fd2 = open(fileb, O_RDWR | O_CREAT | O_TRUNC, 0750);
+ if(fd2 == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ s = socket(PF_UNIX, SOCK_STREAM, 0);
+ if(s == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ sprintf(addr.sun_path, sock);
+
+ unlink(addr.sun_path);
+ if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ perror("bind");
+ exit(1);
+ }
+
+ if(listen(s, 5) == -1) {
+ perror("listen");
+ exit(1);
+ }
+
+ {
+ int x;
+ int baddrsize = 0;
+ struct sockaddr_un baddr;
+ struct msghdr msg = {NULL, 0, NULL, 0, 0, 0, 0};
+ struct cmsghdr *cmsg;
+ char buf[CMSG_SPACE(sizeof(int) * 2)];
+ struct iovec iov[1];
+
+ memset(&baddr, 0, sizeof(baddr));
+ x = accept(s, (struct sockaddr *)&baddr, &baddrsize);
+ if(x == -1) {
+ perror("accept");
+ exit(1);
+ }
+
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 2);
+ ((int *)CMSG_DATA(cmsg))[0] = fd1;
+ ((int *)CMSG_DATA(cmsg))[1] = fd2;
+
+ iov[0].iov_base = "hello";
+ iov[0].iov_len = 6;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if(sendmsg(x, &msg, 0) == -1) {
+ perror("sendmsg");
+ exit(1);
+ }
+ }
+}
+
+void
+client ()
+{
+ int s, fd1 = -1, fd2 = -1, size, count = 0, ret;
+ struct sockaddr_un addr;
+ struct iovec iov[1];
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(int) * 2)];
+ } control_un;
+ struct msghdr msg = { NULL, 0, iov, 1, control_un.control,
+ sizeof(control_un), 0 };
+ struct cmsghdr *cmsg = &control_un.cm;
+ char buf[1024];
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = sizeof(buf);
+
+ s = socket(PF_UNIX, SOCK_STREAM, 0);
+ if(s == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ addr.sun_family = AF_UNIX;
+ sprintf(addr.sun_path, sock);
+
+ do {
+ count++;
+ ret = connect(s, (struct sockaddr *)&addr, sizeof(addr));
+ if(ret == -1) sleep(1);
+ } while (count < 10 && ret == -1);
+
+ if(ret == -1) {
+ perror("connect");
+ exit(1);
+ }
+
+ if((size = recvmsg(s, &msg, 0)) == -1) {
+ perror("recvmsg");
+ exit(1);
+ }
+
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ while(cmsg) {
+ if(cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(int) * 2)) {
+ fd1 = ((int *)CMSG_DATA(cmsg))[0];
+ fd2 = ((int *)CMSG_DATA(cmsg))[1];
+ }
+
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+
+ if(fd1 != -1) write(fd1, "Yeah 1\n", 8);
+ if(fd2 != -1) write(fd2, "Yeah 2\n", 8);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int pid, status;
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ pid = getpid();
+ sprintf(filea, "/tmp/data1.%d", pid);
+ sprintf(fileb, "/tmp/data2.%d", pid);
+ sprintf(sock, "/tmp/sock.%d", pid);
+
+ if((pid = fork()) == 0) {
+ server();
+ return 0;
+ }
+
+ client();
+
+ wait(&status);
+
+ unlink(filea);
+ unlink(fileb);
+ unlink(sock);
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_cmsg.stderr.exp b/corecheck/tests/fdleak_cmsg.stderr.exp
new file mode 100644
index 0000000..b229587
--- /dev/null
+++ b/corecheck/tests/fdleak_cmsg.stderr.exp
@@ -0,0 +1,61 @@
+
+
+FILE DESCRIPTORS: 8 open at exit.
+Open AF_UNIX socket .: /tmp/sock
+ at 0x........: __libc_accept (...libc...)
+ by 0x........: main (fdleak_cmsg.c:170)
+
+Open AF_UNIX socket .: /tmp/sock
+ at 0x........: __socket (in /...libc...)
+ by 0x........: main (fdleak_cmsg.c:170)
+
+Open file descriptor .: /tmp/data2
+ at 0x........: __libc_open (...libc...)
+ by 0x........: main (fdleak_cmsg.c:170)
+
+Open file descriptor .: /tmp/data1
+ at 0x........: __libc_open (...libc...)
+ by 0x........: main (fdleak_cmsg.c:170)
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+
+FILE DESCRIPTORS: 7 open at exit.
+Open file descriptor .: /tmp/data2
+ at 0x........: __libc_recvmsg (...libc...)
+ by 0x........: main (fdleak_cmsg.c:174)
+
+Open file descriptor .: /tmp/data1
+ at 0x........: __libc_recvmsg (...libc...)
+ by 0x........: main (fdleak_cmsg.c:174)
+
+Open AF_UNIX socket .: <unknown>
+ at 0x........: __socket (in /...libc...)
+ by 0x........: main (fdleak_cmsg.c:174)
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_cmsg.vgtest b/corecheck/tests/fdleak_cmsg.vgtest
new file mode 100644
index 0000000..9dcc793
--- /dev/null
+++ b/corecheck/tests/fdleak_cmsg.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_cmsg
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_creat.c b/corecheck/tests/fdleak_creat.c
new file mode 100644
index 0000000..9a2aaf1
--- /dev/null
+++ b/corecheck/tests/fdleak_creat.c
@@ -0,0 +1,21 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+ char filename[24];
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ sprintf(filename, "/tmp/file.%d\n", getpid());
+ creat(filename, 0);
+ unlink(filename);
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_creat.stderr.exp b/corecheck/tests/fdleak_creat.stderr.exp
new file mode 100644
index 0000000..adb93bd
--- /dev/null
+++ b/corecheck/tests/fdleak_creat.stderr.exp
@@ -0,0 +1,23 @@
+
+
+FILE DESCRIPTORS: 5 open at exit.
+Open file descriptor .: /tmp/file
+
+ at 0x........: __libc_creat (...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_creat.vgtest b/corecheck/tests/fdleak_creat.vgtest
new file mode 100644
index 0000000..40a58a5
--- /dev/null
+++ b/corecheck/tests/fdleak_creat.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_creat
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_dup.c b/corecheck/tests/fdleak_dup.c
new file mode 100644
index 0000000..db8a287
--- /dev/null
+++ b/corecheck/tests/fdleak_dup.c
@@ -0,0 +1,19 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+ int s;
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ s = open("/dev/null", O_RDONLY);
+ dup(s);
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_dup.stderr.exp b/corecheck/tests/fdleak_dup.stderr.exp
new file mode 100644
index 0000000..3ba15a8
--- /dev/null
+++ b/corecheck/tests/fdleak_dup.stderr.exp
@@ -0,0 +1,27 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open file descriptor .: /dev/null
+ at 0x........: __dup (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: /dev/null
+ at 0x........: __libc_open (...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_dup.vgtest b/corecheck/tests/fdleak_dup.vgtest
new file mode 100644
index 0000000..818e6a6
--- /dev/null
+++ b/corecheck/tests/fdleak_dup.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_dup
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_dup2.c b/corecheck/tests/fdleak_dup2.c
new file mode 100644
index 0000000..bcc8ff4
--- /dev/null
+++ b/corecheck/tests/fdleak_dup2.c
@@ -0,0 +1,23 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+ int s1;
+ int s2;
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ s1 = open("/dev/null", O_RDONLY);
+ s2 = open("/dev/null", O_RDONLY);
+
+ dup2(s1, 20);
+ dup2(s1, s2);
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_dup2.stderr.exp b/corecheck/tests/fdleak_dup2.stderr.exp
new file mode 100644
index 0000000..cd001c7
--- /dev/null
+++ b/corecheck/tests/fdleak_dup2.stderr.exp
@@ -0,0 +1,32 @@
+
+
+FILE DESCRIPTORS: 7 open at exit.
+Open file descriptor .: /dev/null
+ at 0x........: __dup2 (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: /dev/null
+ at 0x........: __dup2 (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: /dev/null
+ at 0x........: __libc_open (...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_dup2.vgtest b/corecheck/tests/fdleak_dup2.vgtest
new file mode 100644
index 0000000..9296361
--- /dev/null
+++ b/corecheck/tests/fdleak_dup2.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_dup2
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_fcntl.c b/corecheck/tests/fdleak_fcntl.c
new file mode 100644
index 0000000..e2ea4f4
--- /dev/null
+++ b/corecheck/tests/fdleak_fcntl.c
@@ -0,0 +1,20 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+ int s1;
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ s1 = open("/dev/null", O_RDONLY);
+ if(fcntl(s1, F_DUPFD, s1) == -1) perror("fcntl");
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_fcntl.stderr.exp b/corecheck/tests/fdleak_fcntl.stderr.exp
new file mode 100644
index 0000000..270d35b
--- /dev/null
+++ b/corecheck/tests/fdleak_fcntl.stderr.exp
@@ -0,0 +1,26 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open file descriptor .: /dev/null
+ at 0x........: __libc_fcntl (...libc...)
+ by 0x........: main (fdleak_fcntl.c:18)
+
+Open file descriptor .: /dev/null
+ at 0x........: __libc_open (...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_fcntl.vgtest b/corecheck/tests/fdleak_fcntl.vgtest
new file mode 100644
index 0000000..62dda6e
--- /dev/null
+++ b/corecheck/tests/fdleak_fcntl.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_fcntl
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_ipv4.c b/corecheck/tests/fdleak_ipv4.c
new file mode 100644
index 0000000..98d28ae
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.c
@@ -0,0 +1,109 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+void
+server ()
+{
+ int s, x;
+ struct sockaddr_in baddr;
+ struct sockaddr_in addr;
+ int baddrsize = sizeof(baddr);
+ int one = 1;
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if(s == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = 12321;
+
+ if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ perror("bind");
+ exit(1);
+ }
+
+ if(listen(s, 5) == -1) {
+ perror("listen");
+ exit(1);
+ }
+
+ memset(&baddr, 0, sizeof(baddr));
+ x = accept(s, (struct sockaddr *)&baddr, &baddrsize);
+ if(x == -1) {
+ perror("accept");
+ exit(1);
+ }
+
+ write(x, "hello", 6);
+}
+
+void
+client ()
+{
+ int s, count = 0, ret;
+ struct sockaddr_in addr;
+ char buf[1024];
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if(s == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = 12321;
+
+ do {
+ count++;
+ ret = connect(s, (struct sockaddr *)&addr, sizeof(addr));
+ if(ret == -1) sleep(1);
+ } while (count < 10 && ret == -1);
+
+ if(ret == -1) {
+ perror("connect");
+ exit(1);
+ }
+
+ read(s, buf, sizeof(buf));
+
+ printf("%s\n", buf);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int pid, status;
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ if((pid = fork()) == 0) {
+ server();
+ return 0;
+ }
+
+ client();
+
+ wait(&status);
+
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_ipv4.stderr.exp b/corecheck/tests/fdleak_ipv4.stderr.exp
new file mode 100644
index 0000000..d8cb997
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.stderr.exp
@@ -0,0 +1,45 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open AF_INET socket 4: 127.0.0.1:... <-> 127.0.0.1:...
+ at 0x........: __libc_accept (...libc...)
+ by 0x........: main (fdleak_ipv4.c:100)
+
+Open AF_INET socket 3: 127.0.0.1:... <-> unbound
+ at 0x........: __socket (in /...libc...)
+ by 0x........: main (fdleak_ipv4.c:100)
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+
+FILE DESCRIPTORS: 5 open at exit.
+Open AF_INET socket 3: 127.0.0.1:... <-> 127.0.0.1:...
+ at 0x........: __socket (in /...libc...)
+ by 0x........: main (fdleak_ipv4.c:104)
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_ipv4.stdout.exp b/corecheck/tests/fdleak_ipv4.stdout.exp
new file mode 100644
index 0000000..ce01362
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.stdout.exp
@@ -0,0 +1 @@
+hello
diff --git a/corecheck/tests/fdleak_ipv4.vgtest b/corecheck/tests/fdleak_ipv4.vgtest
new file mode 100644
index 0000000..3000ccf
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_ipv4
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_open.c b/corecheck/tests/fdleak_open.c
new file mode 100644
index 0000000..16830fe
--- /dev/null
+++ b/corecheck/tests/fdleak_open.c
@@ -0,0 +1,15 @@
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ open("/dev/null", O_RDONLY);
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_open.stderr.exp b/corecheck/tests/fdleak_open.stderr.exp
new file mode 100644
index 0000000..782e99e
--- /dev/null
+++ b/corecheck/tests/fdleak_open.stderr.exp
@@ -0,0 +1,22 @@
+
+
+FILE DESCRIPTORS: 5 open at exit.
+Open file descriptor .: /dev/null
+ at 0x........: __libc_open (...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_open.vgtest b/corecheck/tests/fdleak_open.vgtest
new file mode 100644
index 0000000..5a803ba
--- /dev/null
+++ b/corecheck/tests/fdleak_open.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_open
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_pipe.c b/corecheck/tests/fdleak_pipe.c
new file mode 100644
index 0000000..6c2d566
--- /dev/null
+++ b/corecheck/tests/fdleak_pipe.c
@@ -0,0 +1,17 @@
+#include <unistd.h>
+
+int
+main (int argc, char **argv)
+{
+ int fds[2];
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ pipe(fds);
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_pipe.stderr.exp b/corecheck/tests/fdleak_pipe.stderr.exp
new file mode 100644
index 0000000..f31301c
--- /dev/null
+++ b/corecheck/tests/fdleak_pipe.stderr.exp
@@ -0,0 +1,27 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open file descriptor .:
+ at 0x........: __pipe (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .:
+ at 0x........: __pipe (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_pipe.vgtest b/corecheck/tests/fdleak_pipe.vgtest
new file mode 100644
index 0000000..540567f
--- /dev/null
+++ b/corecheck/tests/fdleak_pipe.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_pipe
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_socketpair.c b/corecheck/tests/fdleak_socketpair.c
new file mode 100644
index 0000000..a8ddf9c
--- /dev/null
+++ b/corecheck/tests/fdleak_socketpair.c
@@ -0,0 +1,17 @@
+#include <sys/socket.h>
+
+int
+main (int argc, char **argv)
+{
+ int fds[2];
+
+ /*
+ * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it
+ * now to get consistent results across different releases.
+ */
+
+ close(10);
+
+ socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, fds);
+ return 0;
+}
diff --git a/corecheck/tests/fdleak_socketpair.stderr.exp b/corecheck/tests/fdleak_socketpair.stderr.exp
new file mode 100644
index 0000000..4d86c11
--- /dev/null
+++ b/corecheck/tests/fdleak_socketpair.stderr.exp
@@ -0,0 +1,27 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open AF_UNIX socket .: <unknown>
+ at 0x........: __socketpair (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open AF_UNIX socket .: <unknown>
+ at 0x........: __socketpair (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ...
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+Open file descriptor .: .
+ <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_socketpair.vgtest b/corecheck/tests/fdleak_socketpair.vgtest
new file mode 100644
index 0000000..c737e80
--- /dev/null
+++ b/corecheck/tests/fdleak_socketpair.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_socketpair
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/filter_fdleak b/corecheck/tests/filter_fdleak
new file mode 100755
index 0000000..cfb8096
--- /dev/null
+++ b/corecheck/tests/filter_fdleak
@@ -0,0 +1,26 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+$dir/../../tests/filter_stderr_basic |
+
+# Anonymise addresses
+$dir/../../tests/filter_addresses |
+
+# Anonymise line numbers in mac_replace_strmem.c
+sed "s/mac_replace_strmem.c:[0-9]\+/mac_replace_strmem.c:.../" |
+
+$dir/../../tests/filter_test_paths |
+
+# Anonymise paths like "(in /foo/bar/libc-baz.so)"
+sed "s/(in \/.*libc.*)$/(in \/...libc...)/" |
+
+# Anonymise paths like "__libc_start_main (../foo/bar/libc-quux.c:129)"
+sed "s/__libc_\(.*\) (.*)$/__libc_\1 (...libc...)/" |
+
+sed s/"^Open AF_UNIX socket [0-9]\+: <unknown>/Open AF_UNIX socket .: <unknown>/" |
+sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]\+: \/dev\/null/Open \\1 .: \/dev\/null/" |
+sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]\+: \/tmp\/\(sock\|data1\|data2\|file\)\.[0-9]\+/Open \\1 .: \/tmp\/\\2/" |
+sed s/"^Open file descriptor [0-9]\+: .*/Open file descriptor .: ./" |
+sed s/"^Open file descriptor [0-9]\+:$/Open file descriptor .:/" |
+sed s/"127.0.0.1:[0-9]\+/127.0.0.1:.../g"
diff --git a/coregrind/docs/coregrind_core.html b/coregrind/docs/coregrind_core.html
index 6ae2802..6afcba2 100644
--- a/coregrind/docs/coregrind_core.html
+++ b/coregrind/docs/coregrind_core.html
@@ -654,6 +654,15 @@
<code>-v</code> option which prints out all used suppression records.
</li><br><p>
+ <li><code>--track-fds=no</code> [default]<br>
+ <code>--track-fds=yes</code>
+ <p>When enabled, Valgrind will print out a list of open file
+ descriptors on exit. Along with each file descriptor, Valgrind
+ prints out a stack backtrace of where the file was opened and any
+ details relating to the file descriptor such as the file name or
+ socket details.
+ <br><p>
+
<li><code>--gdb-attach=no</code> [default]<br>
<code>--gdb-attach=yes</code>
<p>When enabled, Valgrind will pause after every error shown,
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index ecc0506..c6b49b3 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -257,6 +257,9 @@
extern Bool VG_(clo_lowlat_syscalls);
extern Bool VG_(clo_lowlat_signals);
+/* Track open file descriptors? */
+extern Bool VG_(clo_track_fds);
+
/* Should we run __libc_freeres at exit? Sometimes causes crashes.
Default: YES. Note this is subservient to VG_(needs).libc_freeres;
if the latter says False, then the setting of VG_(clo_weird_hacks)
@@ -1574,6 +1577,10 @@
typedef void (*vg_atfork_t)(ThreadId);
extern void VG_(atfork)(vg_atfork_t pre, vg_atfork_t parent, vg_atfork_t child);
+/* fd leakage calls. */
+extern void VG_(init_preopened_fds) ( void );
+extern void VG_(fd_stats) ( void );
+
/* ---------------------------------------------------------------------
Exports of vg_transtab.c
------------------------------------------------------------------ */
diff --git a/coregrind/vg_main.c b/coregrind/vg_main.c
index edea109..d11a426 100644
--- a/coregrind/vg_main.c
+++ b/coregrind/vg_main.c
@@ -557,6 +557,7 @@
Int VG_(clo_backtrace_size) = 4;
Char* VG_(clo_weird_hacks) = NULL;
Bool VG_(clo_run_libc_freeres) = True;
+Bool VG_(clo_track_fds) = False;
Bool VG_(clo_chain_bb) = True;
Bool VG_(clo_show_below_main) = False;
@@ -658,6 +659,8 @@
" --suppressions=<filename> suppress errors described in <filename>\n"
" --gen-suppressions=no|yes print suppressions for errors detected [no]\n"
+" --track-fds=no|yes Track open file descriptors? [no]\n"
+
" --gdb-attach=no|yes start GDB when errors detected? [no]\n"
" --gdb-path=/path/to/gdb path to the GDB to use [/usr/bin/gdb]\n"
" --input-fd=<number> file descriptor for (gdb) input [0=stdin]\n"
@@ -999,6 +1002,11 @@
else if (VG_CLO_STREQ(argv[i], "--run-libc-freeres=no"))
VG_(clo_run_libc_freeres) = False;
+ else if (VG_CLO_STREQ(argv[i], "--track-fds=yes"))
+ VG_(clo_track_fds) = True;
+ else if (VG_CLO_STREQ(argv[i], "--track-fds=no"))
+ VG_(clo_track_fds) = False;
+
else if (VG_CLO_STREQN(15, argv[i], "--sanity-level="))
VG_(sanity_level) = (Int)VG_(atoll)(&argv[i][15]);
@@ -1601,6 +1609,10 @@
/* Set up baseBlock offsets and copy the saved machine's state into it. */
vg_init_baseBlock();
+ /* Search for file descriptors that are inherited from our parent. */
+ if (VG_(clo_track_fds))
+ VG_(init_preopened_fds)();
+
/* Initialise the scheduler, and copy the client's state from
baseBlock into VG_(threads)[1]. Must be before:
- VG_(sigstartup_actions)()
@@ -1675,6 +1687,10 @@
"Warning: pthread scheduler exited due to deadlock");
}
+ /* Print out file descriptor summary and stats. */
+ if(VG_(clo_track_fds))
+ VG_(fd_stats)();
+
if (VG_(needs).core_errors || VG_(needs).skin_errors)
VG_(show_all_errors)();
diff --git a/coregrind/vg_mylibc.c b/coregrind/vg_mylibc.c
index 07144b2..3693d91 100644
--- a/coregrind/vg_mylibc.c
+++ b/coregrind/vg_mylibc.c
@@ -1204,6 +1204,15 @@
return res;
}
+Int VG_(lseek) ( Int fd, Long offset, Int whence)
+{
+ Int res;
+ /* res = lseek( fd, offset, whence ); */
+ res = VG_(do_syscall)(__NR_lseek, fd, (UInt)offset, whence);
+ if (VG_(is_kerror)(res)) res = -1;
+ return res;
+}
+
Int VG_(stat) ( Char* file_name, struct vki_stat* buf )
{
Int res;
@@ -1289,6 +1298,37 @@
}
+/* Support for getrlimit. */
+Int VG_(getrlimit) (Int resource, struct vki_rlimit *rlim)
+{
+ Int res;
+ /* res = getrlimit( resource, rlim ); */
+ res = VG_(do_syscall)(__NR_getrlimit, (UInt)resource, (UInt)rlim);
+ if(VG_(is_kerror)(res)) res = -1;
+ return res;
+}
+
+
+/* Support for getdents. */
+Int VG_(getdents) (UInt fd, struct vki_dirent *dirp, UInt count)
+{
+ Int res;
+ /* res = getdents( fd, dirp, count ); */
+ res = VG_(do_syscall)(__NR_getdents, fd, (UInt)dirp, count);
+ if (VG_(is_kerror)(res)) res = -1;
+ return res;
+}
+
+/* Support for a readlink. */
+Int VG_(readlink) (Char* path, Char* buf, UInt bufsiz)
+{
+ Int res;
+ /* res = readlink( path, buf, bufsiz ); */
+ res = VG_(do_syscall)(__NR_readlink, (UInt)path, (UInt)buf, bufsiz);
+ if (VG_(is_kerror)(res)) res = -1;
+ return res;
+}
+
/* You'd be amazed how many places need to know the current pid. */
Int VG_(getpid) ( void )
{
@@ -1618,9 +1658,12 @@
/usr/src/linux-2.4.9-31 */
/* kernel, ./include/linux/net.h */
-#define SYS_SOCKET 1 /* sys_socket(2) */
-#define SYS_CONNECT 3 /* sys_connect(2) */
-#define SYS_SEND 9 /* sys_send(2) */
+#define SYS_SOCKET 1 /* sys_socket(2) */
+#define SYS_CONNECT 3 /* sys_connect(2) */
+#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
+#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
+#define SYS_SEND 9 /* sys_send(2) */
+#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
typedef UInt __u32;
@@ -1630,7 +1673,6 @@
};
/* kernel, include/linux/socket.h */
-typedef unsigned short vki_sa_family_t;
#define AF_INET 2 /* Internet IP Protocol */
#define MSG_NOSIGNAL 0x4000 /* Do not generate SIGPIPE */
@@ -1822,6 +1864,48 @@
return res;
}
+Int VG_(getsockname) ( Int sd, struct vki_sockaddr *name, Int *namelen)
+{
+ Int res;
+ UInt args[3];
+ args[0] = sd;
+ args[1] = (UInt)name;
+ args[2] = (UInt)namelen;
+ res = VG_(do_syscall)(__NR_socketcall, SYS_GETSOCKNAME, (UInt)&args);
+ if(VG_(is_kerror)(res))
+ res = -1;
+ return res;
+}
+
+Int VG_(getpeername) ( Int sd, struct vki_sockaddr *name, Int *namelen)
+{
+ Int res;
+ UInt args[3];
+ args[0] = sd;
+ args[1] = (UInt)name;
+ args[2] = (UInt)namelen;
+ res = VG_(do_syscall)(__NR_socketcall, SYS_GETPEERNAME, (UInt)&args);
+ if(VG_(is_kerror)(res))
+ res = -1;
+ return res;
+}
+
+Int VG_(getsockopt) ( Int sd, Int level, Int optname, void *optval,
+ Int *optlen)
+{
+ Int res;
+ UInt args[5];
+ args[0] = sd;
+ args[1] = (UInt)level;
+ args[2] = (UInt)optname;
+ args[3] = (UInt)optval;
+ args[4] = (UInt)optlen;
+ res = VG_(do_syscall)(__NR_socketcall, SYS_GETSOCKOPT, (UInt)&args);
+ if(VG_(is_kerror)(res))
+ res = -1;
+ return res;
+}
+
/*--------------------------------------------------------------------*/
/*--- end vg_mylibc.c ---*/
diff --git a/coregrind/vg_syscalls.c b/coregrind/vg_syscalls.c
index cebcef7..7f28d5c 100644
--- a/coregrind/vg_syscalls.c
+++ b/coregrind/vg_syscalls.c
@@ -257,6 +257,291 @@
return False;
}
+/* One of these is allocated for each open file descriptor. */
+
+typedef struct OpenFd
+{
+ Int fd; /* The file descriptor */
+ Char *pathname; /* NULL if not a regular file or unknown */
+ ExeContext *where; /* NULL if inherited from parent */
+ struct OpenFd *next, *prev;
+} OpenFd;
+
+/* List of allocated file descriptors. */
+
+static OpenFd *allocated_fds;
+
+/* Count of open file descriptors. */
+
+static int fd_count = 0;
+
+/* Given a file descriptor, attempt to deduce it's filename. To do this,
+ we use /proc/self/fd/<FD>. If this doesn't point to a file, or if it
+ doesn't exist, we just return NULL. Otherwise, we return a pointer
+ to the file name, which the caller is responsible for freeing. */
+
+static
+Char *resolve_fname(Int fd)
+{
+ char tmp[28], buf[PATH_MAX];
+
+ VG_(sprintf)(tmp, "/proc/self/fd/%d", fd);
+ VG_(memset)(buf, 0, PATH_MAX);
+
+ if(VG_(readlink)(tmp, buf, PATH_MAX) == -1)
+ return NULL;
+
+ return ((buf[0] == '/') ? VG_(strdup)(buf) : NULL);
+}
+
+
+/* Note the fact that a file descriptor was just closed. */
+
+static
+void record_fd_close(Int tid, Int fd)
+{
+ OpenFd *i = allocated_fds;
+
+ while(i) {
+ if(i->fd == fd) {
+ if(i->prev)
+ i->prev->next = i->next;
+ else
+ allocated_fds = i->next;
+ if(i->next)
+ i->next->prev = i->prev;
+ if(i->pathname)
+ VG_(free) (i->pathname);
+ VG_(free) (i);
+ fd_count--;
+ break;
+ }
+ i = i->next;
+ }
+}
+
+/* Note the fact that a file descriptor was just opened. If the
+ tid is -1, this indicates an inherited fd. If the pathname is NULL,
+ this either indicates a non-standard file (i.e. a pipe or socket or
+ some such thing) or that we don't know the filename. If the fd is
+ already open, then we're probably doing a dup2() to an existing fd,
+ so just overwrite the existing one. */
+
+static
+void record_fd_open(Int tid, Int fd, char *pathname)
+{
+ OpenFd *i;
+
+ /* Check to see if this fd is already open. */
+ i = allocated_fds;
+ while (i) {
+ if (i->fd == fd) {
+ if (i->pathname) VG_(free)(i->pathname);
+ break;
+ }
+ i = i->next;
+ }
+
+ /* Not already one: allocate an OpenFd */
+ if (i == NULL) {
+ i = VG_(malloc)(sizeof(OpenFd));
+
+ i->prev = NULL;
+ i->next = allocated_fds;
+ if(allocated_fds) allocated_fds->prev = i;
+ allocated_fds = i;
+ fd_count++;
+ }
+
+ i->fd = fd;
+ i->pathname = pathname;
+ i->where = (tid == -1) ? NULL : VG_(get_ExeContext)(tid);
+}
+
+static
+Char *unix2name(struct sockaddr_un *sa, UInt len, Char *name)
+{
+ if(sa == NULL || len == 0 || sa->sun_path[0] == '\0') {
+ VG_(sprintf)(name, "<unknown>");
+ } else {
+ VG_(sprintf)(name, "%s", sa->sun_path);
+ }
+
+ return name;
+}
+
+static
+Char *inet2name(struct sockaddr_in *sa, UInt len, Char *name)
+{
+ if(sa == NULL || len == 0) {
+ VG_(sprintf)(name, "<unknown>");
+ } else {
+ UInt addr = sa->sin_addr.s_addr;
+
+ if (addr == 0) {
+ VG_(sprintf)(name, "<unbound>");
+ } else {
+ VG_(sprintf)(name, "%u.%u.%u.%u:%u",
+ addr & 0xFF, (addr>>8) & 0xFF,
+ (addr>>16) & 0xFF, (addr>>24) & 0xFF,
+ sa->sin_port);
+ }
+ }
+
+ return name;
+}
+
+
+/*
+ * Try get some details about a socket.
+ */
+
+static void
+getsockdetails(int fd)
+{
+ union u {
+ struct sockaddr a;
+ struct sockaddr_in in;
+ struct sockaddr_un un;
+ } laddr;
+ socklen_t llen;
+
+ llen = sizeof(laddr);
+ VG_(memset)(&laddr, 0, llen);
+
+ if(VG_(getsockname)(fd, (struct vki_sockaddr *)&(laddr.a), &llen) != -1) {
+ switch(laddr.a.sa_family) {
+ case AF_INET: {
+ static char lname[32];
+ static char pname[32];
+ struct sockaddr_in paddr;
+ socklen_t plen = sizeof(struct sockaddr_in);
+
+ if(VG_(getpeername)(fd, (struct vki_sockaddr *)&paddr, &plen) != -1) {
+ VG_(message)(Vg_UserMsg, "Open AF_INET socket %d: %s <-> %s", fd,
+ inet2name(&(laddr.in), llen, lname),
+ inet2name(&paddr, plen, pname));
+ } else {
+ VG_(message)(Vg_UserMsg, "Open AF_INET socket %d: %s <-> unbound",
+ fd, inet2name(&(laddr.in), llen, lname));
+ }
+ return;
+ }
+ case AF_UNIX: {
+ static char lname[256];
+ VG_(message)(Vg_UserMsg, "Open AF_UNIX socket %d: %s", fd,
+ unix2name(&(laddr.un), llen, lname));
+ return;
+ }
+ default:
+ VG_(message)(Vg_UserMsg, "Open pf-%d socket %d:",
+ laddr.a.sa_family, fd);
+ return;
+ }
+ }
+
+ VG_(message)(Vg_UserMsg, "Open socket %d:", fd);
+}
+
+
+/* Dump out a summary, and optionally a more detailed list, of open file
+ descriptors. */
+
+void VG_(fd_stats) ()
+{
+ OpenFd *i = allocated_fds;
+
+ VG_(message)(Vg_UserMsg,
+ "FILE DESCRIPTORS: %d open at exit.", fd_count);
+
+ while(i) {
+ if(i->pathname) {
+ VG_(message)(Vg_UserMsg, "Open file descriptor %d: %s", i->fd,
+ i->pathname);
+ } else {
+ int val;
+ socklen_t len = sizeof(val);
+
+ if(VG_(getsockopt)(i->fd, SOL_SOCKET, SO_TYPE, &val, &len) == -1) {
+ VG_(message)(Vg_UserMsg, "Open file descriptor %d:", i->fd);
+ } else {
+ getsockdetails(i->fd);
+ }
+ }
+
+ if(i->where) {
+ VG_(pp_ExeContext)(i->where);
+ VG_(message)(Vg_UserMsg, "");
+ } else {
+ VG_(message)(Vg_UserMsg, " <inherited from parent>");
+ VG_(message)(Vg_UserMsg, "");
+ }
+
+ i = i->next;
+ }
+
+ VG_(message)(Vg_UserMsg, "");
+}
+
+/* If /proc/self/fd doesn't exist for some weird reason (like you've
+ got a kernel that doesn't have /proc support compiled in), then we
+ need to find out what file descriptors we inherited from our parent
+ process the hard way - by checking each fd in turn. */
+
+static
+void do_hacky_preopened()
+{
+ struct vki_rlimit lim;
+ unsigned int count;
+ int i;
+
+ if(VG_(getrlimit) (VKI_RLIMIT_NOFILE, &lim) == -1) {
+ /* Hmm. getrlimit() failed. Now we're screwed, so just choose
+ an arbitrarily high number. 1024 happens to be the limit in
+ the 2.4 kernels. */
+ count = 1024;
+ } else {
+ count = lim.rlim_cur;
+ }
+
+ for (i = 0; i < count; i++)
+ if(VG_(fcntl)(i, VKI_F_GETFL, 0) != -1)
+ record_fd_open(-1, i, NULL);
+}
+
+/* Initialize the list of open file descriptors with the file descriptors
+ we inherited from out parent process. */
+
+void VG_(init_preopened_fds)()
+{
+ int f, ret;
+ struct vki_dirent d;
+
+ f = VG_(open)("/proc/self/fd", VKI_O_RDONLY, 0);
+ if(f == -1) {
+ do_hacky_preopened();
+ return;
+ }
+
+ while((ret = VG_(getdents)(f, &d, sizeof(d))) != 0) {
+ if(ret == -1)
+ goto out;
+
+ if(VG_(strcmp)(d.d_name, ".") && VG_(strcmp)(d.d_name, "..")) {
+ int fno = VG_(atoll)(d.d_name);
+
+ if(fno != f)
+ if(VG_(clo_track_fds))
+ record_fd_open(-1, fno, resolve_fname(fno));
+ }
+
+ VG_(lseek)(f, d.d_off, VKI_SEEK_SET);
+ }
+
+out:
+ VG_(close)(f);
+}
+
static
UInt get_shm_size ( Int shmid )
{
@@ -341,6 +626,27 @@
(Addr)msg->msg_control, msg->msg_controllen );
}
+void check_cmsg_for_fds(Int tid, struct msghdr *msg)
+{
+ struct cmsghdr *cm = CMSG_FIRSTHDR(msg);
+
+ while (cm) {
+ if (cm->cmsg_level == SOL_SOCKET &&
+ cm->cmsg_type == SCM_RIGHTS ) {
+ int *fds = (int *) CMSG_DATA(cm);
+ int fdc = (cm->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))
+ / sizeof(int);
+ int i;
+
+ for (i = 0; i < fdc; i++)
+ if(VG_(clo_track_fds))
+ record_fd_open (tid, fds[i], resolve_fname(fds[i]));
+ }
+
+ cm = CMSG_NXTHDR(msg, cm);
+ }
+}
+
static
void pre_mem_read_sockaddr ( ThreadId tid,
Char *description,
@@ -1369,6 +1675,10 @@
res = -VKI_EBADF;
}
+POST(close)
+{
+ if(VG_(clo_track_fds)) record_fd_close(tid, arg1);
+}
PRE(dup)
{
@@ -1382,6 +1692,9 @@
if (!fd_allowed(res, "dup", tid)) {
VG_(close)(res);
res = -VKI_EMFILE;
+ } else {
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, resolve_fname(res));
}
}
@@ -1398,6 +1711,8 @@
MAYBE_PRINTF("SYSCALL[%d] dup2 ( %d, %d ) = %d\n",
VG_(getpid)(),
arg1, arg2, res);
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, resolve_fname(res));
}
PRE(fcntl)
@@ -1406,6 +1721,13 @@
MAYBE_PRINTF("fcntl ( %d, %d, %d )\n",arg1,arg2,arg3);
}
+POST(fcntl)
+{
+ if (arg2 == VKI_F_DUPFD)
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, resolve_fname(res));
+}
+
PRE(fchdir)
{
/* int fchdir(int fd); */
@@ -1428,9 +1750,15 @@
PRE(fcntl64)
{
- /* I don't know what the prototype for this is supposed to be. */
- /* ??? int fcntl(int fd, int cmd); */
- MAYBE_PRINTF("fcntl64 (?!) ( %d, %d )\n", arg1,arg2);
+ /* int fcntl64(int fd, int cmd, int arg); */
+ MAYBE_PRINTF("fcntl64 ( %d, %d, %d )\n", arg1,arg2,arg3);
+}
+
+POST(fcntl64)
+{
+ if (arg2 == VKI_F_DUPFD)
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, resolve_fname(res));
}
PRE(fstat)
@@ -3003,6 +3331,9 @@
if (!fd_allowed(res, "open", tid)) {
VG_(close)(res);
res = -VKI_EMFILE;
+ } else {
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, VG_(strdup)((Char*)arg1));
}
MAYBE_PRINTF("%d\n",res);
}
@@ -3044,6 +3375,9 @@
if (!fd_allowed(res, "creat", tid)) {
VG_(close)(res);
res = -VKI_EMFILE;
+ } else {
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, VG_(strdup)((Char*)arg1));
}
MAYBE_PRINTF("%d\n",res);
}
@@ -3065,8 +3399,13 @@
VG_(close)(p[0]);
VG_(close)(p[1]);
res = -VKI_EMFILE;
- } else
+ } else {
VG_TRACK( post_mem_write, arg1, 2*sizeof(int) );
+ if(VG_(clo_track_fds)) {
+ record_fd_open(tid, p[0], NULL);
+ record_fd_open(tid, p[1], NULL);
+ }
+ }
MAYBE_PRINTF("SYSCALL[%d] pipe --> %d (rd %d, wr %d)\n",
VG_(getpid)(), res,
@@ -3563,12 +3902,19 @@
case SYS_SOCKETPAIR:
/* XXX TODO: check return fd against VG_MAX_FD */
VG_TRACK( post_mem_write, ((UInt*)arg2)[3], 2*sizeof(int) );
+ if(VG_(clo_track_fds)) {
+ record_fd_open(tid, ((UInt*)((UInt*)arg2)[3])[0], NULL);
+ record_fd_open(tid, ((UInt*)((UInt*)arg2)[3])[1], NULL);
+ }
break;
case SYS_SOCKET:
if (!fd_allowed(res, "socket", tid)) {
VG_(close)(res);
res = -VKI_EMFILE;
+ } else {
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, NULL);
}
break;
@@ -3593,6 +3939,8 @@
if (addr_p != (Addr)NULL)
buf_and_len_post_check ( tid, res, addr_p, addrlen_p,
"socketcall.accept(addrlen_out)" );
+ if(VG_(clo_track_fds))
+ record_fd_open(tid, res, NULL);
}
break;
}
@@ -3673,6 +4021,7 @@
struct msghdr *msg = (struct msghdr *)((UInt *)arg2)[ 1 ];
msghdr_foreachfield( tid, msg, post_mem_write_recvmsg );
+ check_cmsg_for_fds( tid, msg );
break;
}
@@ -4222,15 +4571,15 @@
SYSB_(chown32, False),
SYSB_(lchown32, False),
SYSB_(chown, False),
- SYSB_(close, False),
+ SYSBA(close, False),
SYSBA(dup, False),
SYSBA(dup2, False),
- SYSB_(fcntl, True),
+ SYSBA(fcntl, True),
SYSB_(fchdir, False),
SYSB_(fchown32, False),
SYSB_(fchown, False),
SYSB_(fchmod, False),
- SYSB_(fcntl64, True),
+ SYSBA(fcntl64, True),
SYSBA(fstat, False),
SYSBA(fork, False),
SYSB_(fsync, True),
diff --git a/include/vg_kerneliface.h b/include/vg_kerneliface.h
index fcfc71d..0be5f2b 100644
--- a/include/vg_kerneliface.h
+++ b/include/vg_kerneliface.h
@@ -330,6 +330,10 @@
#define VKI_O_DIRECTORY 0200000 /* must be a directory */
#define VKI_O_NOFOLLOW 0400000 /* don't follow links */
+#define VKI_SEEK_SET 0
+#define VKI_SEEK_CUR 1
+#define VKI_SEEK_END 2
+
/* Copied from linux-2.4.19/include/linux/stat.h */
#define VKI_S_IRWXU 00700
@@ -633,6 +637,41 @@
#define VKI_CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force VKI_CLONE_PTRACE on this clone */
#define VKI_CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */
+/* This is the structure passed to the getdents syscall. */
+/*
+ * linux/dirent.h
+ */
+typedef struct vki_dirent {
+ long d_ino;
+ long d_off;
+ unsigned short d_reclen;
+ char d_name[256];
+} vki_dirent;
+
+
+
+/* This is the structure passed to the getrlimit syscall. */
+/*
+ * bits/resource.h
+ */
+typedef struct vki_rlimit {
+ unsigned long rlim_cur;
+ unsigned long rlim_max;
+} vki_rlimit;
+
+#define VKI_RLIMIT_NOFILE 7
+
+/* Socket stuff. */
+/*
+ * sys/socket.h
+ */
+typedef unsigned short vki_sa_family_t;
+struct vki_sockaddr {
+ vki_sa_family_t sa_family; /* Address family. */
+ char sa_data[14]; /* Address data. */
+};
+
+
#endif /* ndef __VG_KERNELIFACE_H */
/*--------------------------------------------------------------------*/
diff --git a/include/vg_skin.h b/include/vg_skin.h
index 0101eaa..ba23074 100644
--- a/include/vg_skin.h
+++ b/include/vg_skin.h
@@ -362,6 +362,9 @@
/* Looks up VG_(client_envp) */
extern Char* VG_(getenv) ( Char* name );
+/* Get client resource limit*/
+extern Int VG_(getrlimit) ( Int resource, struct vki_rlimit *rlim );
+
/* Crude stand-in for the glibc system() call. */
extern Int VG_(system) ( Char* cmd );
@@ -420,6 +423,8 @@
/* ------------------------------------------------------------------ */
/* unistd.h, fcntl.h, sys/stat.h */
+extern Int VG_(getdents)( UInt fd, struct vki_dirent *dirp, UInt count );
+extern Int VG_(readlink)( Char* path, Char* buf, UInt bufsize );
extern Int VG_(getpid) ( void );
extern Int VG_(getppid) ( void );
extern Int VG_(getpgrp) ( void );
@@ -429,6 +434,7 @@
extern Int VG_(open) ( const Char* pathname, Int flags, Int mode );
extern Int VG_(read) ( Int fd, void* buf, Int count);
extern Int VG_(write) ( Int fd, const void* buf, Int count);
+extern Int VG_(lseek) ( Int fd, Long offset, Int whence);
extern void VG_(close) ( Int fd );
extern Int VG_(pipe) ( Int fd[2] );
@@ -514,6 +520,14 @@
extern Int VG_(waitpid) ( Int pid, Int *status, Int options );
/* ------------------------------------------------------------------ */
+/* socket.h. */
+
+extern Int VG_(getsockname) ( Int sd, struct vki_sockaddr *name, Int *namelen);
+extern Int VG_(getpeername) ( Int sd, struct vki_sockaddr *name, Int *namelen);
+extern Int VG_(getsockopt) ( Int sd, Int level, Int optname, void *optval,
+ Int *optlen);
+
+/* ------------------------------------------------------------------ */
/* other, randomly useful functions */
extern UInt VG_(read_millisecond_timer) ( void );