mjb@brunix.UUCP (05/13/83)
>>> 4.1a, 4.1c, 4.2 BSD ONLY (NOT 4.1) <<< Description ----------- Writing 64K or more to a pipe in a single write() statement will ultimately cause the system to crash (with "panic: sbflush 2"). How to recreate --------------- Well I wouldn't advise running it, but the following C program will do it: main() { int pipefd[2]; char data[64*1024]; pipe(pipefd); write(pipefd[1], data, 64*1024); /* Shouldn't get here, right? WRONG! */ /* (it should hang because the pipe's not that big, */ /* and no one is reading it) */ close(pipefd[1]); while (read(pipefd[0], data, 1) > 0) /* do nothing */; close(pipefd[0]); /* SURPRISE! your system just crashed. */ } Analysis -------- There is a bug in the socket sending routine (sosend()) whereby it doesn't ever do partial writes to the socket. So when you tell it to write 64KB, by golly, it just jams the data in the pipe without regard for the arbitrary pipe size limit of 4KB. This in itself is not that bad (after all, we have 6MB of memory!), but, alas, the size of the data queued in the socket is kept in a short int. So 64KB of buffers have been allocated, but the count has wrapped to 0. The read statement doesn't do anything. But on closing the 'read' half of the pipe, the system crashes because it can't figure out how to de-allocate the 64KB worth of memory buffers. To Fix ------ Well, you can tell your users not to do big writes to pipes, or you can install the following fix to /sys/sys/socket.c (it's been running here for over a month with no problems). This is 4.1a code, look for the BRUNIX ifdefs. /* socket.c 4.43 82/08/31 */ . . . sosend(so, asa) register struct socket *so; struct sockaddr *asa; { struct mbuf *top = 0; register struct mbuf *m, **mp = ⊤ register u_int len; int error = 0, space, s; #ifdef BRUNIX struct sockbuf sendtempbuf; #endif . . . space = sbspace(&so->so_snd); if (space <= 0 || sosendallatonce(so) && space < u.u_count) { if (so->so_state & SS_NBIO) snderr(EWOULDBLOCK); sbunlock(&so->so_snd); sbwait(&so->so_snd); splx(s); goto restart; } splx(s); #ifdef BRUNIX sendtempbuf = so->so_snd; #endif while (u.u_count && space > 0) { MGET(m, 1); . . . iomove(mtod(m, caddr_t), len, B_WRITE); m->m_len = len; *mp = m; mp = &m->m_next; #ifdef BRUNIX sballoc(&sendtempbuf, m); space = sbspace(&sendtempbuf); #else !BRUNIX space = sbspace(&so->so_snd); #endif } goto again; release: . . . } I'm Glad You Asked That ----------------------- Who would want to write more than 64KB to a pipe at a time? Graphics people shuffling bitmaps, that's who! Mike Braca, Brown CS, ..!{decvax,ihnp4}!brunix!mjb, mjb.brown@udel-relay