Date: Sun, 11 Apr 1999 23:06:40 +0100 (BST) From: Tony Finch <dot@dotat.at> To: FreeBSD-gnats-submit@freebsd.org Subject: kern/11988: recvmsg with a cmsghdr but no iovec is broken Message-ID: <199904112206.XAA02365@shirt.www.demon.net>
next in thread | raw e-mail | index | archive | help
>Number: 11988 >Category: kern >Synopsis: recvmsg with a cmsghdr but no iovec is broken >Confidential: no >Severity: critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Wed Jun 2 02:30:00 PDT 1999 >Closed-Date: >Last-Modified: >Originator: Tony Finch >Release: FreeBSD 3.1-STABLE i386 >Organization: Demon Internet Ltd >Environment: FreeBSD shirt.www.demon.net 3.1-STABLE FreeBSD 3.1-STABLE #15: Thu Apr 1 17:31:19 BST 1999 root@shirt.www.demon.net:/usr/src/sys/compile/SHIRT i386 >Description: I have a program (included below) which opens a TCP listen socket and forks a child with which it communicates over a unix domain socketpair. When a TCP connection is received it passes the new fd to the child which deals with the connection while the parent returns to accept(). The fd is passed using sendmsg() with an empty msg_iov. If the child process does not provide a pointer to a iovec in the msghdr, or if the sum of the iov_len fields is zero, then the child only receives one file descriptor over the socket and subsequent recvmsg()s do not fill in the msg_control block. Furthermore, if the program is killed with SIGINT between the first (successful) connection and the second connection (which would cause the child to exit owing to EBADF) the machine reboots. I don't have a panic message to show because my test machine is a couple of miles away and doesn't like the serial console server. The program does work if the receive iovec has more than zero space. >How-To-Repeat: #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/uio.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <time.h> extern const char *__progname; static void vwarn(const char *message, va_list ap) { int err = errno; fprintf(stderr, "%s (%d): ", __progname, (int)getpid()); vfprintf(stderr, message, ap); if(message[strlen(message)-1] == ':') fprintf(stderr, " %s (%d)\n", strerror(err), err); else fprintf(stderr, "\n"); } #define DO_VWARN \ va_list ap; \ va_start(ap, message); \ vwarn(message, ap); \ va_end(ap) static void warn(const char *message, ...) { DO_VWARN; } static void die(const char *message, ...) { DO_VWARN; exit(1); } static void debug_msg(struct msghdr *msg) { warn("msg = %p", msg); warn("msg->msg_name = %p", msg->msg_name); warn("msg->msg_namelen = %d", msg->msg_namelen); warn("msg->msg_iov = %p", msg->msg_iov); warn("msg->msg_iovlen = %d", msg->msg_iovlen); if(msg->msg_iov) { warn("msg->msg_iov->iov_base = %p", msg->msg_iov->iov_base); warn("msg->msg_iov->iov_len = %d", msg->msg_iov->iov_len); if(msg->msg_iov->iov_base) warn("*msg->msg_iov->iov_base = %d", *msg->msg_iov->iov_base); } warn("msg->msg_control = %p", msg->msg_control); warn("msg->msg_controllen = %d", msg->msg_controllen); warn("msg->msg_flags = %d", msg->msg_flags); warn("((struct cmsghdr *)msg->msg_control)->cmsg_level = %d", ((struct cmsghdr *)msg->msg_control)->cmsg_level); warn("((struct cmsghdr *)msg->msg_control)->cmsg_type = %d", ((struct cmsghdr *)msg->msg_control)->cmsg_type); warn("((struct cmsghdr *)msg->msg_control)->cmsg_len = %d", ((struct cmsghdr *)msg->msg_control)->cmsg_len); warn("*(int *)CMSG_DATA((struct cmsghdr *)msg->msg_control) = %d", *(int *)CMSG_DATA((struct cmsghdr *)msg->msg_control)); } static void child_process(void) { int n, fd; FILE *fp; char byte; struct iovec iov; struct msghdr msg; struct fdcmsg { struct cmsghdr cmsg; int fd; } cmsg; fd_set rfds; msg.msg_name = NULL; msg.msg_namelen = 0; #if 0 /* if this code is enabled the bug does not appear */ msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = &byte; iov.iov_len = 1; #elif 1 /* this and the next two sections all exhibit the bug */ msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = &byte; iov.iov_len = 0; #elif 0 msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = NULL; iov.iov_len = 0; #else msg.msg_iov = NULL; msg.msg_iovlen = 0; iov.iov_base = NULL; iov.iov_len = 0; #endif msg.msg_control = (void *)&cmsg; msg.msg_controllen = sizeof(cmsg); msg.msg_flags = 0; for(;;) { FD_ZERO(&rfds); FD_SET(3, &rfds); if(select(4, &rfds, NULL, NULL, NULL) < 0) die("select:"); if(!FD_ISSET(3, &rfds)) exit(1); byte = 255; cmsg.cmsg.cmsg_level = 0; cmsg.cmsg.cmsg_type = 0; cmsg.cmsg.cmsg_len = sizeof(cmsg); *(int *)CMSG_DATA(&cmsg.cmsg) = -1; debug_msg(&msg); n = recvmsg(3, &msg, 0); if(n < 0) die("recvmsg:"); else warn("recvmsg = %d", n); debug_msg(&msg); fd = *(int *)CMSG_DATA(&cmsg.cmsg); if(write(fd, "wibble\n", 7) < 7) die("write:"); fp = fdopen(fd, "w"); if(!fp) die("fdopen:"); fprintf(fp, "connection to pid %d\n", getpid()); fclose(fp); } } static int make_child(void) { int pid; int fd[2]; if(socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0) die("socketpair:"); switch(pid = fork()) { case -1: die("fork:"); case 0: close(fd[0]); dup2(fd[1], 3); close(fd[1]); child_process(); exit(0); default: warn("started pid %d", pid); close(fd[1]); return(fd[0]); } } static int make_sock(int port, int backlog) { int s; int one = 1; struct sockaddr_in sa; s = socket(PF_INET, SOCK_STREAM, 0); if(s < 0) die("socket:"); if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)) < 0) die("setsockopt SO_REUSEADDR:"); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = INADDR_ANY; if(bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) die("bind:"); if(listen(s, backlog) < 0) die("listen:"); return(s); } int main(int argc, char *argv[]) { int sock, child, conn, n; char byte; struct iovec iov; struct msghdr msg; struct fdcmsg { struct cmsghdr cmsg; int fd; } cmsg; setvbuf(stderr, NULL, _IOLBF, 0); sock = make_sock(5000, 10); child = make_child(); msg.msg_name = NULL; msg.msg_namelen = 0; /* these alternatives don't make any difference */ #if 0 msg.msg_iov = &iov; msg.msg_iovlen = 1; #else msg.msg_iov = NULL; msg.msg_iovlen = 0; #endif msg.msg_control = (void *)&cmsg; msg.msg_controllen = sizeof(cmsg); msg.msg_flags = 0; iov.iov_base = &byte; iov.iov_len = 1; byte = 0; cmsg.cmsg.cmsg_level = SOL_SOCKET; cmsg.cmsg.cmsg_type = SCM_RIGHTS; cmsg.cmsg.cmsg_len = sizeof(cmsg); for(;;) { conn = accept(sock, NULL, 0); if(conn < 0) { warn("accept:"); continue; } else warn("accept = %d", conn); *(int *)CMSG_DATA(&cmsg.cmsg) = conn; debug_msg(&msg); n = sendmsg(child, &msg, 0); if(n < 0) warn("sendmsg:"); else warn("sendmsg = %d", n); n = close(conn); warn("close = %d", n); } } >Fix: >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199904112206.XAA02365>