NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/40696: FD leak by recvmsg when receiving too much file descriptors with SCM_RIGHTS.
>Number: 40696
>Category: kern
>Synopsis: FD leak by recvmsg when receiving too much file descriptors
>with SCM_RIGHTS.
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Fri Feb 20 04:45:00 +0000 2009
>Originator: Tanaka Akira
>Release: NetBSD 4.0.1/amd64
>Organization:
AIST
>Environment:
NetBSD netbsd.tky.aist.go.jp 4.0.1 NetBSD 4.0.1 (GENERIC) #0: Tue Oct 7
21:00:39 PDT 2008
builds@wb28:/home/builds/ab/netbsd-4-0-1-RELEASE/amd64/200810080053Z-obj/home/builds/ab/netbsd-4-0-1-RELEASE/src/sys/arch/amd64/compile/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 alocates a FD but doesn't record them.
This means the application cannot close the FD which is not reported.
>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 170 wd / 3869056 drwxr-xr-x 512 r
akr a.out 170 0 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 1 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 2 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 3* unix stream ffff800004d20700 <->
ffff800004d20680
akr a.out 170 4* unix stream ffff800004d20680 <->
ffff800004d20700
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 170 wd / 3869056 drwxr-xr-x 512 r
akr a.out 170 0 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 1 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 2 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 3* unix stream ffff800004d20700 <->
ffff800004d20680
akr a.out 170 4* unix stream ffff800004d20680 <->
ffff800004d20700
akr a.out 170 5 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 6 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 7 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 8 / 3568894 crw--w---- ttyp0 rw
akr a.out 170 14 / 3568894 crw--w---- ttyp0 rw
This program sends 10 FDs via UNIX domain socket pair and
receives some of them using recvmsg with small control message buffer.
The result shows:
* kernel allocates 5 FDs (5, 6, 7, 8 and 14)
* 4 of them are reported to the application (5, 6, 7, 8)
* The last FD, 14, is not reported to the application.
The application cannot close 14.
>Fix:
Home |
Main Index |
Thread Index |
Old Index