summaryrefslogtreecommitdiff
path: root/sys/ufs
diff options
context:
space:
mode:
authorPhilip Guenther <guenther@cvs.openbsd.org>2013-11-23 19:07:52 +0000
committerPhilip Guenther <guenther@cvs.openbsd.org>2013-11-23 19:07:52 +0000
commit20d81826eb388e54f2b5452bd0b8cb76a4d21173 (patch)
treea5867f6559a3a6bacfdc8cd93ef2887a908718be /sys/ufs
parentb778f66ba5b14ca8a13ddd6611b156f9346be37e (diff)
Verify that the directory entry being looked at is entirely present in
the read buffer before trying to process it, so that invalidated or bogus offsets can't trigger access past the end of the read buffer. Also, zero out the dirent buffer to avoid leaking stack garbage to userspace. Problem pointed out by Pedro Martelletto (pedro (at) ambientworks.net) ok deraadt@
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ufs/ufs_vnops.c35
1 files changed, 27 insertions, 8 deletions
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index 09620ac10b5..8de4a89dac3 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ufs_vnops.c,v 1.109 2013/09/22 17:14:55 guenther Exp $ */
+/* $OpenBSD: ufs_vnops.c,v 1.110 2013/11/23 19:07:51 guenther Exp $ */
/* $NetBSD: ufs_vnops.c,v 1.18 1996/05/11 18:28:04 mycroft Exp $ */
/*
@@ -1379,7 +1379,8 @@ ufs_readdir(void *v)
char __pad[roundup(sizeof(struct dirent), 8)];
} u;
off_t off = uio->uio_offset;
- struct direct *dp, *edp;
+ struct direct *dp;
+ char *edp;
caddr_t diskbuf;
size_t count, entries;
int readcnt, error;
@@ -1416,12 +1417,22 @@ ufs_readdir(void *v)
error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
readcnt -= auio.uio_resid;
dp = (struct direct *)diskbuf;
- edp = (struct direct *)&diskbuf[readcnt];
- while (error == 0 && dp < edp) {
- if (dp->d_reclen <= offsetof(struct direct, d_name)) {
- error = EIO;
- break;
- }
+ edp = &diskbuf[readcnt];
+
+ memset(&u, 0, sizeof(u));
+
+ /*
+ * While
+ * - we haven't failed to VOP_READ or uiomove()
+ * - there's space in the read buf for the head of an entry
+ * - that entry has a valid d_reclen, and
+ * - there's space for the *entire* entry
+ * then we're good to process this one.
+ */
+ while (error == 0 &&
+ (char *)dp + offsetof(struct direct, d_name) < edp &&
+ dp->d_reclen > offsetof(struct direct, d_name) &&
+ (char *)dp + dp->d_reclen <= edp) {
u.dn.d_reclen = roundup(dp->d_namlen+1 +
offsetof(struct dirent, d_name), 8);
if (u.dn.d_reclen > uio->uio_resid)
@@ -1446,6 +1457,14 @@ ufs_readdir(void *v)
error = uiomove(&u.dn, u.dn.d_reclen, uio);
dp = (struct direct *)((char *)dp + dp->d_reclen);
}
+
+ /*
+ * If there was room for an entry in what we read but its
+ * d_reclen is bogus, fail
+ */
+ if ((char *)dp + offsetof(struct direct, d_name) < edp &&
+ dp->d_reclen <= offsetof(struct direct, d_name))
+ error = EIO;
free(diskbuf, M_TEMP);
uio->uio_offset = off;