From f9ea43d8961cf1f7da4f038c2a0ba05a804a887d Mon Sep 17 00:00:00 2001 From: Robert Walsh Date: Mon, 17 Nov 2003 17:45:00 +0000 Subject: [PATCH] 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 --- corecheck/tests/Makefile.am | 26 +- corecheck/tests/fdleak_cmsg.c | 182 ++++++++++ corecheck/tests/fdleak_cmsg.stderr.exp | 61 ++++ corecheck/tests/fdleak_cmsg.vgtest | 3 + corecheck/tests/fdleak_creat.c | 21 ++ corecheck/tests/fdleak_creat.stderr.exp | 23 ++ corecheck/tests/fdleak_creat.vgtest | 3 + corecheck/tests/fdleak_dup.c | 19 + corecheck/tests/fdleak_dup.stderr.exp | 27 ++ corecheck/tests/fdleak_dup.vgtest | 3 + corecheck/tests/fdleak_dup2.c | 23 ++ corecheck/tests/fdleak_dup2.stderr.exp | 32 ++ corecheck/tests/fdleak_dup2.vgtest | 3 + corecheck/tests/fdleak_fcntl.c | 20 + corecheck/tests/fdleak_fcntl.stderr.exp | 26 ++ corecheck/tests/fdleak_fcntl.vgtest | 3 + corecheck/tests/fdleak_ipv4.c | 109 ++++++ corecheck/tests/fdleak_ipv4.stderr.exp | 45 +++ corecheck/tests/fdleak_ipv4.stdout.exp | 1 + corecheck/tests/fdleak_ipv4.vgtest | 3 + corecheck/tests/fdleak_open.c | 15 + corecheck/tests/fdleak_open.stderr.exp | 22 ++ corecheck/tests/fdleak_open.vgtest | 3 + corecheck/tests/fdleak_pipe.c | 17 + corecheck/tests/fdleak_pipe.stderr.exp | 27 ++ corecheck/tests/fdleak_pipe.vgtest | 3 + corecheck/tests/fdleak_socketpair.c | 17 + corecheck/tests/fdleak_socketpair.stderr.exp | 27 ++ corecheck/tests/fdleak_socketpair.vgtest | 3 + corecheck/tests/filter_fdleak | 26 ++ coregrind/docs/coregrind_core.html | 9 + coregrind/vg_include.h | 7 + coregrind/vg_main.c | 16 + coregrind/vg_mylibc.c | 92 ++++- coregrind/vg_syscalls.c | 363 ++++++++++++++++++- include/vg_kerneliface.h | 39 ++ include/vg_skin.h | 14 + 37 files changed, 1319 insertions(+), 14 deletions(-) create mode 100644 corecheck/tests/fdleak_cmsg.c create mode 100644 corecheck/tests/fdleak_cmsg.stderr.exp create mode 100644 corecheck/tests/fdleak_cmsg.vgtest create mode 100644 corecheck/tests/fdleak_creat.c create mode 100644 corecheck/tests/fdleak_creat.stderr.exp create mode 100644 corecheck/tests/fdleak_creat.vgtest create mode 100644 corecheck/tests/fdleak_dup.c create mode 100644 corecheck/tests/fdleak_dup.stderr.exp create mode 100644 corecheck/tests/fdleak_dup.vgtest create mode 100644 corecheck/tests/fdleak_dup2.c create mode 100644 corecheck/tests/fdleak_dup2.stderr.exp create mode 100644 corecheck/tests/fdleak_dup2.vgtest create mode 100644 corecheck/tests/fdleak_fcntl.c create mode 100644 corecheck/tests/fdleak_fcntl.stderr.exp create mode 100644 corecheck/tests/fdleak_fcntl.vgtest create mode 100644 corecheck/tests/fdleak_ipv4.c create mode 100644 corecheck/tests/fdleak_ipv4.stderr.exp create mode 100644 corecheck/tests/fdleak_ipv4.stdout.exp create mode 100644 corecheck/tests/fdleak_ipv4.vgtest create mode 100644 corecheck/tests/fdleak_open.c create mode 100644 corecheck/tests/fdleak_open.stderr.exp create mode 100644 corecheck/tests/fdleak_open.vgtest create mode 100644 corecheck/tests/fdleak_pipe.c create mode 100644 corecheck/tests/fdleak_pipe.stderr.exp create mode 100644 corecheck/tests/fdleak_pipe.vgtest create mode 100644 corecheck/tests/fdleak_socketpair.c create mode 100644 corecheck/tests/fdleak_socketpair.stderr.exp create mode 100644 corecheck/tests/fdleak_socketpair.vgtest create mode 100755 corecheck/tests/filter_fdleak diff --git a/corecheck/tests/Makefile.am b/corecheck/tests/Makefile.am index 89a3659ed..8d8ad6ba4 100644 --- a/corecheck/tests/Makefile.am +++ b/corecheck/tests/Makefile.am @@ -8,6 +8,15 @@ noinst_SCRIPTS = filter_stderr 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 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ 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 @@ AM_CXXFLAGS = $(AM_CFLAGS) # 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 000000000..a6dccb102 --- /dev/null +++ b/corecheck/tests/fdleak_cmsg.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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 000000000..b2295879f --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 .: + at 0x........: __socket (in /...libc...) + by 0x........: main (fdleak_cmsg.c:174) + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..9dcc7939b --- /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 000000000..9a2aaf1e1 --- /dev/null +++ b/corecheck/tests/fdleak_creat.c @@ -0,0 +1,21 @@ +#include +#include +#include + +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 000000000..adb93bd57 --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..40a58a5a2 --- /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 000000000..db8a28746 --- /dev/null +++ b/corecheck/tests/fdleak_dup.c @@ -0,0 +1,19 @@ +#include +#include + +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 000000000..3ba15a850 --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..818e6a617 --- /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 000000000..bcc8ff4a1 --- /dev/null +++ b/corecheck/tests/fdleak_dup2.c @@ -0,0 +1,23 @@ +#include +#include + +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 000000000..cd001c771 --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..929636183 --- /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 000000000..e2ea4f4a0 --- /dev/null +++ b/corecheck/tests/fdleak_fcntl.c @@ -0,0 +1,20 @@ +#include +#include +#include + +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 000000000..270d35b39 --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..62dda6e1b --- /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 000000000..98d28aee2 --- /dev/null +++ b/corecheck/tests/fdleak_ipv4.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 000000000..d8cb9970d --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..ce0136250 --- /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 000000000..3000ccfe6 --- /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 000000000..16830fe2a --- /dev/null +++ b/corecheck/tests/fdleak_open.c @@ -0,0 +1,15 @@ +#include + +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 000000000..782e99e4d --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..5a803ba5c --- /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 000000000..6c2d566d1 --- /dev/null +++ b/corecheck/tests/fdleak_pipe.c @@ -0,0 +1,17 @@ +#include + +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 000000000..f31301c62 --- /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 .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..540567f8c --- /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 000000000..a8ddf9c10 --- /dev/null +++ b/corecheck/tests/fdleak_socketpair.c @@ -0,0 +1,17 @@ +#include + +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 000000000..4d86c117e --- /dev/null +++ b/corecheck/tests/fdleak_socketpair.stderr.exp @@ -0,0 +1,27 @@ + + +FILE DESCRIPTORS: 6 open at exit. +Open AF_UNIX socket .: + at 0x........: __socketpair (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open AF_UNIX socket .: + at 0x........: __socketpair (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: . + + + +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 000000000..c737e80a0 --- /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 000000000..cfb8096f2 --- /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]\+: /Open AF_UNIX socket .: /" | +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 6ae280224..6afcba22c 100644 --- a/coregrind/docs/coregrind_core.html +++ b/coregrind/docs/coregrind_core.html @@ -654,6 +654,15 @@ not Cachegrind. -v option which prints out all used suppression records.

+

  • --track-fds=no [default]
    + --track-fds=yes +

    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. +

    +

  • --gdb-attach=no [default]
    --gdb-attach=yes

    When enabled, Valgrind will pause after every error shown, diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h index ecc0506d3..c6b49b340 100644 --- a/coregrind/vg_include.h +++ b/coregrind/vg_include.h @@ -257,6 +257,9 @@ extern Bool VG_(clo_assume_24); 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 @@ extern Bool VG_(is_kerror) ( Int res ); 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 edea109b2..d11a4265f 100644 --- a/coregrind/vg_main.c +++ b/coregrind/vg_main.c @@ -557,6 +557,7 @@ Int VG_(clo_dump_error) = 0; 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 @@ static void usage ( void ) " --suppressions= suppress errors described in \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= file descriptor for (gdb) input [0=stdin]\n" @@ -999,6 +1002,11 @@ static void process_cmd_line_options ( void ) 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 @@ void VG_(main) ( void ) /* 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 @@ void VG_(main) ( void ) "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 07144b2bb..3693d9124 100644 --- a/coregrind/vg_mylibc.c +++ b/coregrind/vg_mylibc.c @@ -1204,6 +1204,15 @@ Int VG_(write) ( Int fd, const void* buf, Int count) 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 @@ Char* VG_(getenv) ( Char* varname ) } +/* 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 @@ void VG_(ssort)( void* base, UInt nmemb, UInt size, /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 @@ struct vki_in_addr { }; /* 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 @@ Int VG_(write_socket)( Int sd, void *msg, Int count ) 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 cebcef7ae..7f28d5c93 100644 --- a/coregrind/vg_syscalls.c +++ b/coregrind/vg_syscalls.c @@ -257,6 +257,291 @@ Bool VG_(is_kerror) ( Int res ) 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/. 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, ""); + } 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, ""); + } else { + UInt addr = sa->sin_addr.s_addr; + + if (addr == 0) { + VG_(sprintf)(name, ""); + } 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, " "); + 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 @@ void msghdr_foreachfield ( (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 @@ PRE(close) res = -VKI_EBADF; } +POST(close) +{ + if(VG_(clo_track_fds)) record_fd_close(tid, arg1); +} PRE(dup) { @@ -1382,6 +1692,9 @@ POST(dup) 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 @@ POST(dup2) 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 @@ PRE(fcntl) 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(fchmod) 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 @@ POST(open) 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 @@ POST(creat) 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 @@ POST(pipe) 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 @@ POST(socketcall) 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 @@ POST(socketcall) 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 @@ POST(socketcall) 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 @@ static const struct sys_info sys_info[] = { 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 fcfc71de2..0be5f2bdd 100644 --- a/include/vg_kerneliface.h +++ b/include/vg_kerneliface.h @@ -330,6 +330,10 @@ struct vki_ucontext { #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 @@ typedef struct vki_modify_ldt_ldt_s { #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 0101eaa24..ba23074e3 100644 --- a/include/vg_skin.h +++ b/include/vg_skin.h @@ -362,6 +362,9 @@ extern void VG_(skin_panic) ( Char* str ); /* 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 @@ extern Int VG_(log2) ( Int x ); /* ------------------------------------------------------------------ */ /* 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_(setpgid) ( Int pid, Int pgrp ); 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] ); @@ -513,6 +519,14 @@ extern Int VG_(ksigpending) ( vki_ksigset_t* set ); 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 );