Date: Sun, 23 Jul 2000 20:10:02 -0700 (PDT) From: Bruce Evans <bde@zeta.org.au> To: freebsd-bugs@FreeBSD.org Subject: Re: kern/19407: Panic running linux binary on ext2fs Message-ID: <200007240310.UAA96945@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR kern/19407; it has been noted by GNATS. From: Bruce Evans <bde@zeta.org.au> To: "Mark W. Krentel" <krentel@dreamscape.com> Cc: freebsd-gnats-submit@FreeBSD.ORG Subject: Re: kern/19407: Panic running linux binary on ext2fs Date: Mon, 24 Jul 2000 13:02:40 +1000 (EST) On Thu, 6 Jul 2000, Mark W. Krentel wrote: > I've run some more experiments and I've narrowed the problem somewhat. > > Using the Slackware 7 live file system, I tar-copied /cdrom/live/bin > onto ufs and ext2fs partitions. Then I ran Slackware's ls from ufs, > cdrom and ext2fs and listed directories on ufs, cdrom and ext2fs. > Sometimes it worked ok, sometimes the output of ls was corrupt (too > few files), and the pattern is quite clear. > > directory listed on > binary on ufs cdrom ext2fs > ufs ok corrupt corrupt > cdrom ok corrupt corrupt > ext2fs ok corrupt corrupt I found some of the problems using these hints. There were 2 serious bugs in ext2_readdir(): writing far beyond the end of the cookie buffer, and reading a little beyond the end of the directory buffer. There don't seem to be any problems with the Linuxulator. It just asks ext2_readdir() for cookies. Then cookie processing is usually fatal. Similarly for readdir() on an nfs-mounted ext2fs filesystem. Overrunning the directory buffer can cause panics and wrong results from readdir(3) even for native binaries, but this problem doesn't usually occur for native binaries because they use an adequate buffer size (4K). Linux binaries trigger the bug by using a too-small buffer size (512 bytes). This size makes Linux's ls (an old (1997) RedHat version) take about 4 times as much system time as FreeBSD's ls even on ufs filesystems. getdirentries(2) claims that the correct size is given by stat(2), but Linux's ls apparently doesn't know this, and in any case the correct size is a little larger than the filesystem blocksize for ext2fs, since ext2_readdir() expands some directory entries. Try these fixes: Index: ext2_lookup.c =================================================================== RCS file: /home/ncvs/src/sys/gnu/ext2fs/ext2_lookup.c,v retrieving revision 1.24 diff -c -2 -r1.24 ext2_lookup.c *** ext2_lookup.c 2000/05/05 09:57:57 1.24 --- ext2_lookup.c 2000/07/24 02:09:03 *************** *** 153,166 **** struct iovec aiov; caddr_t dirbuf; int readcnt; ! u_quad_t startoffset = uio->uio_offset; ! count = uio->uio_resid; /* legyenek boldogok akik akarnak ... */ ! uio->uio_resid = count; ! uio->uio_iov->iov_len = count; ! ! #if 0 ! printf("ext2_readdir called uio->uio_offset %d uio->uio_resid %d count %d \n", ! (int)uio->uio_offset, (int)uio->uio_resid, (int)count); #endif --- 152,175 ---- struct iovec aiov; caddr_t dirbuf; + int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize; int readcnt; ! off_t startoffset = uio->uio_offset; ! count = uio->uio_resid; ! /* ! * Avoid complications for partial directory entries by adjusting ! * the i/o to end at a block boundary. Don't give up (like ufs ! * does) if the initial adjustment gives a negative count, since ! * many callers don't supply a large enough buffer. The correct ! * size is a little larger than DIRBLKSIZ to allow for expansion ! * of directory entries, but some callers just use 512. ! */ ! count -= (uio->uio_offset + count) & (DIRBLKSIZ -1); ! if (count <= 0) ! count += DIRBLKSIZ; ! ! #ifdef EXT2FS_DEBUG ! printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n", ! uio->uio_offset, uio->uio_resid, count); #endif *************** *** 168,171 **** --- 177,181 ---- auio.uio_iov = &aiov; auio.uio_iovcnt = 1; + auio.uio_resid = count; auio.uio_segflg = UIO_SYSSPACE; aiov.iov_len = count; *************** *** 226,231 **** if (!error && ap->a_ncookies != NULL) { ! u_long *cookies; ! u_long *cookiep; off_t off; --- 236,240 ---- if (!error && ap->a_ncookies != NULL) { ! u_long *cookiep, *cookies, *ecookies; off_t off; *************** *** 235,240 **** M_WAITOK); off = startoffset; ! for (dp = (struct ext2_dir_entry_2 *)dirbuf, cookiep = cookies; ! dp < edp; dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) { off += dp->rec_len; --- 244,250 ---- M_WAITOK); off = startoffset; ! for (dp = (struct ext2_dir_entry_2 *)dirbuf, ! cookiep = cookies, ecookies = cookies + ncookies; ! cookiep < ecookies; dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) { off += dp->rec_len; Bruce 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?200007240310.UAA96945>