diff options
author | bde <bde@FreeBSD.org> | 2000-09-12 17:10:39 +0000 |
---|---|---|
committer | bde <bde@FreeBSD.org> | 2000-09-12 17:10:39 +0000 |
commit | f16595f63d83acc00c80c72b954cbcc3197241b4 (patch) | |
tree | e5e802bfb6c2a95d69a264afe2d18e3ad3647232 /sys/gnu | |
parent | 8437d5b6f497e851fd3d65553c3af50667ccfbd4 (diff) | |
download | FreeBSD-src-f16595f63d83acc00c80c72b954cbcc3197241b4.zip FreeBSD-src-f16595f63d83acc00c80c72b954cbcc3197241b4.tar.gz |
Fixed some serious bugs in ext2_readdir():
The cookie buffer was usually overrun by a large amount whenever
cookies were used. Cookies are used by nfs and the Linuxulator, so
this bug usually caused panics whenever an ext2fs filesystem was nfs
mounted or a Linux utility that calls readdir() was run on an ext2fs
filesystem.
The directory buffer was sometimes overrun by a small amount. This
sometimes caused panics and wrong results even for FreeBSD utilities,
but it was usually harmless because FreeBSD utilities use a large
enough buffer size (4K). Linux utilities usually triggered the bug
since they use a too-small buffer size (512 bytes), at least with the
old RedHat utilities that I tested with.
PR: 19407 (this fix is incomplete or for a slightly different bug)
Diffstat (limited to 'sys/gnu')
-rw-r--r-- | sys/gnu/ext2fs/ext2_lookup.c | 33 | ||||
-rw-r--r-- | sys/gnu/fs/ext2fs/ext2_lookup.c | 33 |
2 files changed, 44 insertions, 22 deletions
diff --git a/sys/gnu/ext2fs/ext2_lookup.c b/sys/gnu/ext2fs/ext2_lookup.c index e161534..7909a6c 100644 --- a/sys/gnu/ext2fs/ext2_lookup.c +++ b/sys/gnu/ext2fs/ext2_lookup.c @@ -152,21 +152,32 @@ ext2_readdir(ap) struct uio auio; struct iovec aiov; caddr_t dirbuf; + int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize; int readcnt; - u_quad_t startoffset = uio->uio_offset; + off_t startoffset = uio->uio_offset; - count = uio->uio_resid; /* legyenek boldogok akik akarnak ... */ - uio->uio_resid = count; - uio->uio_iov->iov_len = count; + 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; -#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); +#ifdef EXT2FS_DEBUG + printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n", + uio->uio_offset, uio->uio_resid, count); #endif auio = *uio; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; + auio.uio_resid = count; auio.uio_segflg = UIO_SYSSPACE; aiov.iov_len = count; MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); @@ -225,8 +236,7 @@ printf("ext2_readdir called uio->uio_offset %d uio->uio_resid %d count %d \n", uio->uio_offset = startoffset + (caddr_t)dp - dirbuf; if (!error && ap->a_ncookies != NULL) { - u_long *cookies; - u_long *cookiep; + u_long *cookiep, *cookies, *ecookies; off_t off; if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) @@ -234,8 +244,9 @@ printf("ext2_readdir called uio->uio_offset %d uio->uio_resid %d count %d \n", MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); off = startoffset; - for (dp = (struct ext2_dir_entry_2 *)dirbuf, cookiep = cookies; - dp < edp; + 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; *cookiep++ = (u_long) off; diff --git a/sys/gnu/fs/ext2fs/ext2_lookup.c b/sys/gnu/fs/ext2fs/ext2_lookup.c index e161534..7909a6c 100644 --- a/sys/gnu/fs/ext2fs/ext2_lookup.c +++ b/sys/gnu/fs/ext2fs/ext2_lookup.c @@ -152,21 +152,32 @@ ext2_readdir(ap) struct uio auio; struct iovec aiov; caddr_t dirbuf; + int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize; int readcnt; - u_quad_t startoffset = uio->uio_offset; + off_t startoffset = uio->uio_offset; - count = uio->uio_resid; /* legyenek boldogok akik akarnak ... */ - uio->uio_resid = count; - uio->uio_iov->iov_len = count; + 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; -#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); +#ifdef EXT2FS_DEBUG + printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n", + uio->uio_offset, uio->uio_resid, count); #endif auio = *uio; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; + auio.uio_resid = count; auio.uio_segflg = UIO_SYSSPACE; aiov.iov_len = count; MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); @@ -225,8 +236,7 @@ printf("ext2_readdir called uio->uio_offset %d uio->uio_resid %d count %d \n", uio->uio_offset = startoffset + (caddr_t)dp - dirbuf; if (!error && ap->a_ncookies != NULL) { - u_long *cookies; - u_long *cookiep; + u_long *cookiep, *cookies, *ecookies; off_t off; if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) @@ -234,8 +244,9 @@ printf("ext2_readdir called uio->uio_offset %d uio->uio_resid %d count %d \n", MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); off = startoffset; - for (dp = (struct ext2_dir_entry_2 *)dirbuf, cookiep = cookies; - dp < edp; + 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; *cookiep++ = (u_long) off; |