diff options
author | Philip Guenther <guenther@cvs.openbsd.org> | 2013-11-23 19:07:52 +0000 |
---|---|---|
committer | Philip Guenther <guenther@cvs.openbsd.org> | 2013-11-23 19:07:52 +0000 |
commit | 20d81826eb388e54f2b5452bd0b8cb76a4d21173 (patch) | |
tree | a5867f6559a3a6bacfdc8cd93ef2887a908718be /sys | |
parent | b778f66ba5b14ca8a13ddd6611b156f9346be37e (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')
-rw-r--r-- | sys/ufs/ufs/ufs_vnops.c | 35 |
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; |