Date: Thu, 19 Feb 2009 15:18:17 GMT From: Tanaka Akira <akr@fsij.org> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/131876: FD leak by receiving SCM_RIGHTS by recvmsg with small control message buffer Message-ID: <200902191518.n1JFIHQm006981@www.freebsd.org> Resent-Message-ID: <200902191520.n1JFK1LN034574@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 131876 >Category: kern >Synopsis: FD leak by receiving SCM_RIGHTS by recvmsg with small control message buffer >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Thu Feb 19 15:20:00 UTC 2009 >Closed-Date: >Last-Modified: >Originator: Tanaka Akira >Release: FreeBSD 6.4-RELEASE amd64 >Organization: AIST >Environment: FreeBSD freebsd.tky.aist.go.jp 6.4-RELEASE FreeBSD 6.4-RELEASE #0: Wed Nov 26 08:21:48 UTC 2008 root@palmer.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64 >Description: When recvmsg receives SCM_RIGHTS control message, it allocates file descriptors and report them in the control message buffer given by the application. If the buffer is too small to record the FDs, recvmsg allocates FDs but doesn't record them. This means the application cannot close those FDs which is not reported. i.e. FDs leaks. >How-To-Repeat: % cat tst.c #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/uio.h> #include <unistd.h> #define MAX_FDS 10 #define SEND_FDS 10 #define RECV_FDS 3 int main(int argc, char **argv) { int ret; int sv[2]; struct msghdr msg; struct iovec iov; union { struct cmsghdr header; char bytes[CMSG_SPACE(sizeof(int)*MAX_FDS)]; } cmsg; struct cmsghdr *cmh = &cmsg.header, *c; int *fds; int i; char buf[1024]; char cmdline[1024]; snprintf(cmdline, sizeof(cmdline), "fstat -p %u", (unsigned)getpid()); ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); if (ret == -1) { perror("socketpair"); exit(1); } iov.iov_base = "a"; iov.iov_len = 1; cmh->cmsg_len = CMSG_LEN(sizeof(int)*SEND_FDS); cmh->cmsg_level = SOL_SOCKET; cmh->cmsg_type = SCM_RIGHTS; fds = (int *)CMSG_DATA(cmh); for (i = 0; i < SEND_FDS; i++) { fds[i] = 0; /* stdin */ } msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cmh; msg.msg_controllen = CMSG_SPACE(sizeof(int)*SEND_FDS); msg.msg_flags = 0; ret = sendmsg(sv[0], &msg, 0); if (ret == -1) { perror("sendmsg"); exit(1); } system(cmdline); /* fstat -p $$ before recvmsg */ iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cmh; msg.msg_controllen = CMSG_SPACE(sizeof(int)*RECV_FDS); msg.msg_flags = 0; printf("before recvmsg: msg_controllen=%d\n", msg.msg_controllen); ret = recvmsg(sv[1], &msg, 0); if (ret == -1) { perror("sendmsg"); exit(1); } printf("after recvmsg: msg_controllen=%d\n", msg.msg_controllen); for (c = CMSG_FIRSTHDR(&msg); c != NULL; c = CMSG_NXTHDR(&msg, c)) { if (c->cmsg_len == 0) { printf("cmsg_len is zero\n"); exit(1); } if (c->cmsg_level == SOL_SOCKET && c->cmsg_type == SCM_RIGHTS) { int *fdp, *end; printf("cmsg_len=%d\n", c->cmsg_len); fdp = (int *)CMSG_DATA(c); end = (int *)((char *)c + c->cmsg_len); for (i = 0; fdp+i < end; i++) { printf("fd[%d]=%d\n", i, fdp[i]); } } } system(cmdline); /* fstat -p $$ after recvmsg */ return 0; } % gcc -Wall tst.c % ./a.out USER CMD PID FD MOUNT INUM MODE SZ|DV R/W akr a.out 9822 root / 2 drwxr-xr-x 512 r akr a.out 9822 wd / 32142 drwxr-xr-x 512 r akr a.out 9822 text / 32138 -rwxr-xr-x 9286 r akr a.out 9822 0 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 1 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 2 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 3* local stream ffffff00613da320 <-> ffffff00613da258 akr a.out 9822 4* local stream ffffff00613da258 <-> ffffff00613da320 before recvmsg: msg_controllen=32 after recvmsg: msg_controllen=32 cmsg_len=56 fd[0]=5 fd[1]=6 fd[2]=7 fd[3]=8 fd[4]=0 fd[5]=0 fd[6]=0 fd[7]=0 fd[8]=0 fd[9]=0 USER CMD PID FD MOUNT INUM MODE SZ|DV R/W akr a.out 9822 root / 2 drwxr-xr-x 512 r akr a.out 9822 wd / 32142 drwxr-xr-x 512 r akr a.out 9822 text / 32138 -rwxr-xr-x 9286 r akr a.out 9822 0 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 1 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 2 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 3* local stream ffffff00613da320 <-> ffffff00613da258 akr a.out 9822 4* local stream ffffff00613da258 <-> ffffff00613da320 akr a.out 9822 5 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 6 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 7 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 8 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 9 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 10 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 11 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 12 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 13 /dev 97 crw--w---- ttyp0 rw akr a.out 9822 14 /dev 97 crw--w---- ttyp0 rw This program sends 10 file descriptors via UNIX domain socket pair and receives some of them using a small buffer. This result shows * recvmsg allocates 10 FDs (5-14) * 4 of them (5-8) are reported to the application * 6 of them (9-14) are not reported to the application So the application cannot close the 6 file descriptors. >Fix: >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200902191518.n1JFIHQm006981>