das@eplunix.UUCP (David Steffens) (01/11/90)
Subject: Read/write return EINVAL after 2^31 chars have been read/written. File: sys/sys_inode.c Repeat-by: Try to read more than 2^31 characters from a character device. See read return EINVAL. Fails with write, too. You may prefer inspection -- takes a few hours, even on a Sun4! Discussion: This bug was first discovered in a SunOS4.0.3 binary and traced back to the 4.3bsd (and 4.2bsd) sources. It appears to have been introduced by a too-hasty translation of imprecise English logic into C. What seems to have been expressed in English was: IF the offset is negative AND it isn't a character device OR it isn't the memory device THEN return EINVAL. Unfortunately, when this was coded it became: IF the offset is negative AND EITHER NOT a character device OR NOT the memory device THEN return EINVAL. This causes EINVAL to be returned for EVERY character device EXCEPT the memory device. Seems to me what really should have been expressed (and coded) was: IF the offset is negative AND NEITHER a character device NOR the memory device THEN return EINVAL. Fix: *** sys/sys_inode.c0 Thu Jun 5 03:08:12 1986 --- sys/sys_inode.c Wed Jan 10 14:18:55 1990 *************** *** 94,101 **** panic("rwip"); if (rw == UIO_READ && uio->uio_resid == 0) return (0); if (uio->uio_offset < 0 && ! ((ip->i_mode&IFMT) != IFCHR || mem_no != major(dev))) return (EINVAL); if (rw == UIO_READ) ip->i_flag |= IACC; --- 94,107 ---- panic("rwip"); if (rw == UIO_READ && uio->uio_resid == 0) return (0); + /* + * The following is the correct logic: + * if (uio->uio_offset < 0 && + * !((ip->i_mode&IFMT) == IFCHR || major(dev) == mem_no)) + * DeMorgan's therorem can be used to get a simpler expression, however. + */ if (uio->uio_offset < 0 && ! (ip->i_mode&IFMT) != IFCHR && major(dev) != mem_no) return (EINVAL); if (rw == UIO_READ) ip->i_flag |= IACC; -- {harvard,mit-eddie,think}!eplunix!das David Allan Steffens 243 Charles St., Boston, MA 02114 Eaton-Peabody Laboratory (617) 573-3748 Mass. Eye & Ear Infirmary