Issue #5075 has been reported by Yui NARUSE. ---------------------------------------- Bug #5075: invalid *fdp in Mac OS X and FreeBSD over recvmsg with SCM_RIGHTS http://redmine.ruby-lang.org/issues/5075 Author: Yui NARUSE Status: Assigned Priority: Normal Assignee: Akira Tanaka Category: ext Target version: 1.9.3 ruby -v: ruby 1.9.4dev (2011-07-22 trunk 32604) [x86_64-freebsd8.2] Mac OS X と FreeBSD にて、存在しない fd を close してしまう問題について、 現在 r32598 で応急処置が施されていますが、根本的な原因について、 sys/kern/uipc_socket.c を見るに、 http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/kern/uipc_socket.c?rev=1.340.2.6.2.1;content-type=text%2Fplain;only_with_tag=RELENG_8_2_0_RELEASE * Process one or more MT_CONTROL mbufs present before any data mbufs * in the first mbuf chain on the socket buffer. If MSG_PEEK, we * just copy the data; if !MSG_PEEK, we call into the protocol to * perform externalization (or freeing if controlp == NULL). とあるので、recvmsg に MSG_PEEK を与えた場合は invalid なものが返ってくると思うのですが。 ちなみに、以下のような printf パッチをあてて走らせると、discard_cmsg() に来たものは全て invalid になっています。 diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c index 61e0576..ad44fb4 100644 --- a/ext/socket/ancdata.c +++ b/ext/socket/ancdata.c @@ -1379,6 +1379,7 @@ rb_recvmsg(int fd, struct msghdr *msg, int flags) static void discard_cmsg(struct cmsghdr *cmh, char *msg_end) { + fprintf(stderr, "discard_cmsg-begin\n"); if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_RIGHTS) { int *fdp = (int *)CMSG_DATA(cmh); int *end = (int *)((char *)cmh + cmh->cmsg_len); @@ -1391,12 +1392,18 @@ discard_cmsg(struct cmsghdr *cmh, char *msg_end) */ struct stat buf; if (fstat(*fdp, &buf) == 0) { + fprintf(stderr, "fdp: %d is valid (%p %p %p)\n", *fdp,fdp,end,msg_end); rb_update_max_fd(*fdp); close(*fdp); } + else { + fprintf(stderr, "fdp: %d is invalid (%p %p %p)\n", *fdp,fdp,end,msg_end); + rb_backtrace(); + } fdp++; } } + fprintf(stderr, "discard_cmsg-end\n"); } #endif @@ -1432,6 +1439,7 @@ make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end) (char *)fdp + sizeof(int) <= msg_end) { int fd = *fdp; struct stat stbuf; + fprintf(stderr,"makeiounixr: %d (%p %p %p)\n", *fdp,fdp,end,msg_end); VALUE io; if (fstat(fd, &stbuf) == -1) rb_raise(rb_eSocket, "invalid fd in SCM_RIGHTS"); -- http://redmine.ruby-lang.org