From 20d81826eb388e54f2b5452bd0b8cb76a4d21173 Mon Sep 17 00:00:00 2001 From: Philip Guenther Date: Sat, 23 Nov 2013 19:07:52 +0000 Subject: 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@ --- sys/ufs/ufs/ufs_vnops.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'sys/ufs') 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; -- cgit v1.2.3