summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorbde <bde@FreeBSD.org>2000-09-12 17:10:39 +0000
committerbde <bde@FreeBSD.org>2000-09-12 17:10:39 +0000
commitf16595f63d83acc00c80c72b954cbcc3197241b4 (patch)
treee5e802bfb6c2a95d69a264afe2d18e3ad3647232 /sys
parent8437d5b6f497e851fd3d65553c3af50667ccfbd4 (diff)
downloadFreeBSD-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')
-rw-r--r--sys/gnu/ext2fs/ext2_lookup.c33
-rw-r--r--sys/gnu/fs/ext2fs/ext2_lookup.c33
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;
OpenPOWER on IntegriCloud