pkl@lewey.AIT.COM (Peter K. Lee) (11/22/86)
PRIMARY AFFECTED MODULE: kernel - sys/uipc_socket.c (MANUAL PART NUMBER IF DOCUMENTATION PROBLEM) REPORTED BY: Peter K. Lee ucbvax!hplabs!lewey!pkl COMPANY: American Information Technology, Inc. CONTACT: Peter K. Lee ADDRESS: 10201 Torre Ave, Cupertino CA 95014 PHONE: (408) 252-8713 DETAILED DESCRIPTION OF PROBLEM: If we use readv() to read from a socket (or pipe) and one of the io vectors has a bad iov_base pointer, readv() will return -1 with errno set to EFAULT. However, some of the unreceived data may be lost. The next read to the socket will not get you the data immediately after the point of failure. In soreceive(), after uiomove(), the entire mbuf is discarded no matter the uiomove() is successful or not. DEMONSTRATION PROGRAM / PROCEDURE: Compile the following program (sockbug.c) by "cc sockbug.c". Run it. You will see that readv() on a socket loses the rest of the data after the error in readv(). ---- sockbug.c ---- #include <sys/types.h> #include <sys/uio.h> #include <sys/ioctl.h> #include <sys/file.h> char *alpha = "abcdefghijklmnopqrstuvwxyz"; int alphalen = 26; char bufa[10], bufb[10], bufc[6]; struct iovec iov[3]; int niov = sizeof(iov) / sizeof(iov[0]); extern int errno; main() { usefile(); usepipe(); } usepipe() { int fd[2]; int nread; int on = 1; char next; printf("\nUsing Pipe/sockets\n"); if (pipe(fd) < 0) { perror("pipe"); exit(1); } if (write(fd[1], alpha, alphalen) != alphalen) { perror("write fd[1]"); exit(1); } if (ioctl(fd[0], FIONBIO, &on) < 0) { perror("ioctl(FIONBIO)"); exit(1); } /* * Now, put a bad pointer into iov[1] */ clearbuf(); iov[1].iov_base = (caddr_t)0xafafafaf; readtest(fd[0]); } usefile() { int fd; printf("\nUsing File\n"); fd = open("tempfile", O_RDWR|O_CREAT|O_TRUNC, 0666); if (fd < 0) { perror("usefile: open"); exit(1); } if (write(fd, alpha, alphalen) != alphalen) { perror("usefile: write"); exit(1); } if (lseek(fd, 0L, L_SET) < 0) { perror("usefile: lseek"); exit(1); } clearbuf(); iov[1].iov_base = (caddr_t)0xfafafafa; readtest(fd); } readtest(fd) int fd; { int nread; char next; nread = readv(fd, iov, niov); if (nread < 0) perror("readv"); printf("nread = %d\n", nread); dumpbuf("bufa", bufa, sizeof(bufa)); dumpbuf("bufb", bufb, sizeof(bufb)); dumpbuf("bufc", bufc, sizeof(bufc)); nread = read(fd, &next, 1); if (nread < 0) perror("read"); printf("nread = %d\n", nread); dumpbuf("next", &next, 1); } clearbuf() { bzero(bufa, sizeof(bufa)); iov[0].iov_base = (caddr_t)bufa; iov[0].iov_len = sizeof(bufa); bzero(bufb, sizeof(bufb)); iov[1].iov_base = (caddr_t)bufb; iov[1].iov_len = sizeof(bufb); bzero(bufc, sizeof(bufc)); iov[2].iov_base = (caddr_t)bufc; iov[2].iov_len = sizeof(bufc); } dumpbuf(name, buf, size) char *name; char *buf; int size; { int i; printf("%s: ", name); for (i = 0; i < size; i++) putchar(buf[i]); putchar('\n'); } ---- end sockbug.c ---- PROPOSED SOLUTION: Calculate the actual number of bytes received by uiomove() and discard that number of bytes from receive sockbuf. Here is a context diff. *** /sys/sys/uipc_socket.c Tue Aug 26 00:03:03 1986 --- uipc_socket.c Fri Nov 21 10:23:43 1986 *************** *** 441,446 **** --- 441,447 ---- struct protosw *pr = so->so_proto; struct mbuf *nextrecord; int moff; + int oresid; /* no. of bytes in uio before uiomove */ if (rightsp) *rightsp = 0; *************** *** 548,554 **** while (m && uio->uio_resid > 0 && error == 0) { if (m->m_type != MT_DATA && m->m_type != MT_HEADER) panic("receive 3"); ! len = uio->uio_resid; so->so_state &= ~SS_RCVATMARK; if (tomark && len > tomark) len = tomark; --- 549,555 ---- while (m && uio->uio_resid > 0 && error == 0) { if (m->m_type != MT_DATA && m->m_type != MT_HEADER) panic("receive 3"); ! oresid = len = uio->uio_resid; so->so_state &= ~SS_RCVATMARK; if (tomark && len > tomark) len = tomark; *************** *** 558,563 **** --- 559,565 ---- error = uiomove(mtod(m, caddr_t) + moff, (int)len, UIO_READ, uio); s = splnet(); + len = oresid - uio->uio_resid; /* actual bytes received */ if (len == m->m_len - moff) { if (flags & MSG_PEEK) { m = m->m_next;