diff --git a/sys/vfs/hpfs/hpfs_lookup.c b/sys/vfs/hpfs/hpfs_lookup.c --- a/sys/vfs/hpfs/hpfs_lookup.c +++ b/sys/vfs/hpfs/hpfs_lookup.c @@ -46,6 +46,8 @@ int hpfs_removedirent (struct hpfsmount *, lsn_t, char *, int, int *); +#define HPFS_DIRDEPTH_MAX 64 /* B-tree depth sanity bound */ + /* * This routine traverse the b+ tree representing directory * looking for file named 'name'. Returns buf struct and hpfsdirent @@ -65,21 +67,39 @@ struct dirblk *dp; struct hpfsdirent *dep; lsn_t lsn; - int error, res; + caddr_t dlimit; + int error, res, depth; dprintf(("hpfs_genlookupbyname(0x%x, %s (%d)): \n", dhp->h_no, name, namelen)); lsn = ((alleaf_t *)dhp->h_fn.fn_abd)->al_lsn; + + depth = 0; dive: + if (depth++ > HPFS_DIRDEPTH_MAX) { /* defeat DE_DOWN cycles */ + kprintf("hpfs_genlookupbyname: too deep at lsn 0x%x\n", lsn); + return (EINVAL); + } error = hpfs_breaddirblk (hpmp, lsn, &bp); if (error) return (error); dp = (struct dirblk *) bp->b_data; dep = D_DIRENT(dp); + dlimit = (caddr_t)dp + D_BSIZE; /* hard bound: bread'd size */ while(!(dep->de_flag & DE_END)) { + /* Dirent header + advance must lie entirely in the buffer. + * de_reclen must be >= the fixed header size (this also + * forbids de_reclen==0, which would otherwise spin forever), + * and large enough to hold de_namelen bytes. */ + if ((caddr_t)dep + sizeof(struct hpfsdirent) > dlimit || + dep->de_reclen < sizeof(struct hpfsdirent) || + (caddr_t)dep + dep->de_reclen > dlimit || + dep->de_namelen > dep->de_reclen - sizeof(struct hpfsdirent) + 1) + goto bad; + dprintf(("no: 0x%x, size: %d, name: %2d:%.*s, flag: 0x%x\n", dep->de_fnode, dep->de_size, dep->de_namelen, dep->de_namelen, dep->de_name, dep->de_flag)); @@ -96,6 +116,11 @@ dep = (hpfsdirent_t *)(((caddr_t)dep) + dep->de_reclen); } + /* re-validate the terminator dirent before reading DE_DOWN/DE_DOWNLSN */ + if ((caddr_t)dep + sizeof(struct hpfsdirent) > dlimit || + dep->de_reclen < sizeof(struct hpfsdirent)) + goto bad; + if (dep->de_flag & DE_DOWN) { lsn = DE_DOWNLSN(dep); brelse(bp); @@ -105,6 +130,11 @@ brelse(bp); return (ENOENT); + + bad: + kprintf("hpfs_genlookupbyname: corrupt dirblk at lsn 0x%x\n", lsn); + brelse(bp); + return (EINVAL); } int diff --git a/sys/vfs/hpfs/hpfs_subr.c b/sys/vfs/hpfs/hpfs_subr.c --- a/sys/vfs/hpfs/hpfs_subr.c +++ b/sys/vfs/hpfs/hpfs_subr.c @@ -44,6 +44,8 @@ #include "hpfsmount.h" #include "hpfs_subr.h" +#define HPFS_DIRDEPTH_MAX 64 /* B-tree depth sanity bound */ + u_long hpfs_checksum( u_int8_t *object, @@ -529,6 +531,7 @@ struct dirblk *dp; struct hpfsdirent *dep; lsn_t lsn, olsn; + caddr_t dlimit; int level, error; dprintf(("hpfs_validatetimes(0x%x): [parent: 0x%x] ", @@ -551,6 +554,11 @@ dive: dprintf(("[dive 0x%x] ", lsn)); + if (level > HPFS_DIRDEPTH_MAX) { + kprintf("hpfs_validateparent: too deep at lsn 0x%x\n", lsn); + error = EINVAL; + goto failed; + } if (bp != NULL) brelse(bp); error = bread(dhp->h_devvp, dbtodoff(lsn), D_BSIZE, &bp); @@ -565,6 +573,7 @@ } dep = D_DIRENT(dp); + dlimit = (caddr_t)dp + D_BSIZE; if (olsn) { dprintf(("[restore 0x%x] ", olsn)); @@ -572,10 +581,18 @@ while(!(dep->de_flag & DE_END) ) { if((dep->de_flag & DE_DOWN) && (olsn == DE_DOWNLSN(dep))) - break; + break; + if ((caddr_t)dep + sizeof(struct hpfsdirent) > dlimit || + dep->de_reclen < sizeof(struct hpfsdirent) || + (caddr_t)dep + dep->de_reclen > dlimit) { + kprintf("hpfs_validateparent: corrupt dirblk\n"); + error = EINVAL; + goto failed; + } dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); } + if((dep->de_flag & DE_DOWN) && (olsn == DE_DOWNLSN(dep))) { if (dep->de_flag & DE_END) goto blockdone; @@ -585,6 +602,13 @@ goto readdone; } + if ((caddr_t)dep + sizeof(struct hpfsdirent) > dlimit || + dep->de_reclen < sizeof(struct hpfsdirent) || + (caddr_t)dep + dep->de_reclen > dlimit) { + kprintf("hpfs_validateparent: corrupt dirblk\n"); + error = EINVAL; + goto failed; + } dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); } else { kprintf("hpfs_validatetimes: ERROR! oLSN not found\n"); @@ -607,6 +631,13 @@ goto readdone; } + if ((caddr_t)dep + sizeof(struct hpfsdirent) > dlimit || + dep->de_reclen < sizeof(struct hpfsdirent) || + (caddr_t)dep + dep->de_reclen > dlimit) { + kprintf("hpfs_validateparent: corrupt dirblk\n"); + error = EINVAL; + goto failed; + } dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); }