diff options
author | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2001-04-13 02:39:07 +0000 |
---|---|---|
committer | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2001-04-13 02:39:07 +0000 |
commit | 9bff9fb87213262bc5d1afd9333ad540d9e99c54 (patch) | |
tree | 5797ca5b1e346d54d4606ff7293258862d4b223b | |
parent | 5af2b89b71f9bb495e071539ea9548813c712c7c (diff) |
Fix overflow in superblock. From FreeBSD.
FreeBSD's log:
> The ffs superblock includes a 128-byte region for use by temporary
> in-core pointers to summary information. An array in this region
> (fs_csp) could overflow on filesystems with a very large number of
> cylinder groups (~16000 on i386 with 8k blocks). When this happens,
> other fields in the superblock get corrupted, and fsck refuses to
> check the filesystem.
>
> Solve this problem by replacing the fs_csp array in 'struct fs'
> with a single pointer, and add padding to keep the length of the
> 128-byte region fixed. Update the kernel and userland utilities
> to use just this single pointer.
>
> With this change, the kernel no longer makes use of the superblock
> fields 'fs_csshift' and 'fs_csmask'. Add a comment to newfs/mkfs.c
> to indicate that these fields must be calculated for compatibility
> with older kernels.
art@ ok.
-rw-r--r-- | sbin/dumpfs/dumpfs.c | 8 | ||||
-rw-r--r-- | sbin/fsck_ffs/setup.c | 12 | ||||
-rw-r--r-- | sbin/fsck_ffs/utilities.c | 6 | ||||
-rw-r--r-- | sbin/newfs/mkfs.c | 10 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_vfsops.c | 25 | ||||
-rw-r--r-- | sys/ufs/ffs/fs.h | 38 |
6 files changed, 53 insertions, 46 deletions
diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c index fbd343f159e..9123aa67d42 100644 --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dumpfs.c,v 1.10 1999/07/18 16:03:56 deraadt Exp $ */ +/* $OpenBSD: dumpfs.c,v 1.11 2001/04/13 02:39:06 gluk Exp $ */ /* $NetBSD: dumpfs.c,v 1.12 1997/04/26 05:41:33 lukem Exp $ */ /* @@ -44,7 +44,7 @@ static char copyright[] = #if 0 static char sccsid[] = "@(#)dumpfs.c 8.2 (Berkeley) 2/2/94"; #else -static char rcsid[] = "$OpenBSD: dumpfs.c,v 1.10 1999/07/18 16:03:56 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: dumpfs.c,v 1.11 2001/04/13 02:39:06 gluk Exp $"; #endif #endif /* not lint */ @@ -223,15 +223,15 @@ dumpfs(name) } } printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t"); + afs.fs_csp = calloc(1, afs.fs_cssize); for (i = 0, j = 0; i < afs.fs_cssize; i += afs.fs_bsize, j++) { size = afs.fs_cssize - i < afs.fs_bsize ? afs.fs_cssize - i : afs.fs_bsize; - afs.fs_csp[j] = calloc(1, size); if (lseek(fd, (off_t)(fsbtodb(&afs, (afs.fs_csaddr + j * afs.fs_frag))) * dev_bsize, SEEK_SET) == (off_t)-1) goto err; - if (read(fd, afs.fs_csp[j], size) != size) + if (read(fd, (char *)afs.fs_csp + i, size) != size) goto err; } for (i = 0; i < afs.fs_ncg; i++) { diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index 624efbe3dac..1852b6fdbf5 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: setup.c,v 1.9 2001/04/06 20:43:31 gluk Exp $ */ +/* $OpenBSD: setup.c,v 1.10 2001/04/13 02:39:05 gluk Exp $ */ /* $NetBSD: setup.c,v 1.27 1996/09/27 22:45:19 christos Exp $ */ /* @@ -38,7 +38,7 @@ #if 0 static char sccsid[] = "@(#)setup.c 8.5 (Berkeley) 11/23/94"; #else -static char rcsid[] = "$OpenBSD: setup.c,v 1.9 2001/04/06 20:43:31 gluk Exp $"; +static char rcsid[] = "$OpenBSD: setup.c,v 1.10 2001/04/13 02:39:05 gluk Exp $"; #endif #endif /* not lint */ @@ -328,11 +328,11 @@ setup(dev) * read in the summary info. */ asked = 0; + sblock.fs_csp = calloc(1, sblock.fs_cssize); for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { size = sblock.fs_cssize - i < sblock.fs_bsize ? sblock.fs_cssize - i : sblock.fs_bsize; - sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size); - if (bread(fsreadfd, (char *)sblock.fs_csp[j], + if (bread(fsreadfd, (char *)sblock.fs_csp + i, fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), size) != 0 && !asked) { pfatal("BAD SUMMARY INFORMATION"); @@ -456,8 +456,8 @@ readsb(listerr) altsblock.fs_optim = sblock.fs_optim; altsblock.fs_rotdelay = sblock.fs_rotdelay; altsblock.fs_maxbpg = sblock.fs_maxbpg; - memcpy(altsblock.fs_csp, sblock.fs_csp, - sizeof sblock.fs_csp); + memcpy(altsblock.fs_ocsp, sblock.fs_ocsp, sizeof sblock.fs_ocsp); + altsblock.fs_csp = sblock.fs_csp; altsblock.fs_maxcluster = sblock.fs_maxcluster; altsblock.fs_contigdirs = sblock.fs_contigdirs; altsblock.fs_avgfilesize = sblock.fs_avgfilesize; diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c index fade9cef9b4..78a053d38e6 100644 --- a/sbin/fsck_ffs/utilities.c +++ b/sbin/fsck_ffs/utilities.c @@ -1,4 +1,4 @@ -/* $OpenBSD: utilities.c,v 1.11 2001/03/02 08:33:55 art Exp $ */ +/* $OpenBSD: utilities.c,v 1.12 2001/04/13 02:39:05 gluk Exp $ */ /* $NetBSD: utilities.c,v 1.18 1996/09/27 22:45:20 christos Exp $ */ /* @@ -38,7 +38,7 @@ #if 0 static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93"; #else -static char rcsid[] = "$OpenBSD: utilities.c,v 1.11 2001/03/02 08:33:55 art Exp $"; +static char rcsid[] = "$OpenBSD: utilities.c,v 1.12 2001/04/13 02:39:05 gluk Exp $"; #endif #endif /* not lint */ @@ -233,7 +233,7 @@ flush(fd, bp) if (bp != &sblk) return; for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { - bwrite(fswritefd, (char *)sblock.fs_csp[j], + bwrite(fswritefd, (char *)sblock.fs_csp + i, fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), sblock.fs_cssize - i < sblock.fs_bsize ? sblock.fs_cssize - i : sblock.fs_bsize); diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c index 2368e523f98..aacecf48349 100644 --- a/sbin/newfs/mkfs.c +++ b/sbin/newfs/mkfs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mkfs.c,v 1.16 2001/04/04 22:06:38 gluk Exp $ */ +/* $OpenBSD: mkfs.c,v 1.17 2001/04/13 02:39:06 gluk Exp $ */ /* $NetBSD: mkfs.c,v 1.25 1995/06/18 21:35:38 cgd Exp $ */ /* @@ -38,7 +38,7 @@ #if 0 static char sccsid[] = "@(#)mkfs.c 8.3 (Berkeley) 2/3/94"; #else -static char rcsid[] = "$OpenBSD: mkfs.c,v 1.16 2001/04/04 22:06:38 gluk Exp $"; +static char rcsid[] = "$OpenBSD: mkfs.c,v 1.17 2001/04/13 02:39:06 gluk Exp $"; #endif #endif /* not lint */ @@ -576,6 +576,12 @@ next: sblock.fs_csaddr = cgdmin(&sblock, 0); sblock.fs_cssize = fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); + + /* + * The superblock fields 'fs_csmask' and 'fs_csshift' are no + * longer used. However, we still initialise them so that the + * filesystem remains compatible with old kernels. + */ i = sblock.fs_bsize / sizeof(struct csum); sblock.fs_csmask = ~(i - 1); for (sblock.fs_csshift = 0; i > 1; i >>= 1) diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 10d31c10e67..3ffd6abf98e 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_vfsops.c,v 1.37 2001/04/12 17:16:52 csapuntz Exp $ */ +/* $OpenBSD: ffs_vfsops.c,v 1.38 2001/04/13 02:39:05 gluk Exp $ */ /* $NetBSD: ffs_vfsops.c,v 1.19 1996/02/09 22:22:26 christos Exp $ */ /* @@ -446,7 +446,7 @@ ffs_reload(mountp, cred, p) { register struct vnode *vp, *nvp, *devvp; struct inode *ip; - struct csum *space; + caddr_t space; struct buf *bp; struct fs *fs, *newfs; struct partinfo dpart; @@ -487,7 +487,7 @@ ffs_reload(mountp, cred, p) * new superblock. These should really be in the ufsmount. XXX * Note that important parameters (eg fs_ncg) are unchanged. */ - bcopy(&fs->fs_csp[0], &newfs->fs_csp[0], sizeof(fs->fs_csp)); + newfs->fs_csp = fs->fs_csp; newfs->fs_maxcluster = fs->fs_maxcluster; bcopy(newfs, fs, (u_int)fs->fs_sbsize); if (fs->fs_sbsize < SBSIZE) @@ -500,7 +500,7 @@ ffs_reload(mountp, cred, p) * Step 3: re-read summary information from disk. */ blks = howmany(fs->fs_cssize, fs->fs_fsize); - space = fs->fs_csp[0]; + space = (caddr_t)fs->fs_csp; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) @@ -509,7 +509,8 @@ ffs_reload(mountp, cred, p) NOCRED, &bp); if (error) return (error); - bcopy(bp->b_data, fs->fs_csp[fragstoblks(fs, i)], (u_int)size); + bcopy(bp->b_data, space, (u_int)size); + space += size; brelse(bp); } if ((fs->fs_flags & FS_DOSOFTDEP)) @@ -582,7 +583,7 @@ ffs_mountfs(devvp, mp, p) register struct fs *fs; dev_t dev; struct partinfo dpart; - caddr_t base, space; + caddr_t space; int error, i, blks, size, ronly; int32_t *lp; size_t strsize; @@ -677,7 +678,8 @@ ffs_mountfs(devvp, mp, p) blks = howmany(size, fs->fs_fsize); if (fs->fs_contigsumsize > 0) size += fs->fs_ncg * sizeof(int32_t); - base = space = malloc((u_long)size, M_UFSMNT, M_WAITOK); + space = malloc((u_long)size, M_UFSMNT, M_WAITOK); + fs->fs_csp = (struct csum *)space; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) @@ -685,11 +687,10 @@ ffs_mountfs(devvp, mp, p) error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, cred, &bp); if (error) { - free(base, M_UFSMNT); + free(fs->fs_csp, M_UFSMNT); goto out; } bcopy(bp->b_data, space, (u_int)size); - fs->fs_csp[fragstoblks(fs, i)] = (struct csum *)space; space += size; brelse(bp); bp = NULL; @@ -754,7 +755,7 @@ ffs_mountfs(devvp, mp, p) if (ronly == 0) { if ((fs->fs_flags & FS_DOSOFTDEP) && (error = softdep_mount(devvp, mp, fs, cred)) != 0) { - free(base, M_UFSMNT); + free(fs->fs_csp, M_UFSMNT); free(fs->fs_contigdirs, M_UFSMNT); goto out; } @@ -850,7 +851,7 @@ ffs_unmount(mp, mntflags, p) error = VOP_CLOSE(ump->um_devvp, fs->fs_ronly ? FREAD : FREAD|FWRITE, NOCRED, p); vrele(ump->um_devvp); - free(fs->fs_csp[0], M_UFSMNT); + free(fs->fs_csp, M_UFSMNT); free(fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; @@ -1235,7 +1236,7 @@ ffs_sbupdate(mp, waitfor) * First write back the summary information. */ blks = howmany(fs->fs_cssize, fs->fs_fsize); - space = (caddr_t)fs->fs_csp[0]; + space = (caddr_t)fs->fs_csp; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h index 3719eaba2df..e8e9f0bf3c1 100644 --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -1,4 +1,4 @@ -/* $OpenBSD: fs.h,v 1.10 2001/04/07 17:25:38 millert Exp $ */ +/* $OpenBSD: fs.h,v 1.11 2001/04/13 02:39:04 gluk Exp $ */ /* $NetBSD: fs.h,v 1.6 1995/04/12 21:21:02 mycroft Exp $ */ /* @@ -105,14 +105,19 @@ #define MAXMNTLEN 512 /* - * The limit on the amount of summary information per file system - * is defined by MAXCSBUFS. It is currently parameterized for a - * size of 128 bytes (2 million cylinder groups on machines with - * 32-bit pointers, and 1 million on 64-bit machines). One pointer - * is taken away to point to an array of cluster sizes that is - * computed as cylinder groups are inspected. + * There is a 128-byte region in the superblock reserved for in-core + * pointers to summary information. Originally this included an array + * of pointers to blocks of struct csum; now there are just three + * pointers and the remaining space is padded with fs_ocsp[]. + * + * NOCSPTRS determines the size of this padding. One pointer (fs_csp) + * is taken away to point to a contiguous array of struct csum for + * all cylinder groups; a second (fs_maxcluster) points to an array + * of cluster sizes that is computed as cylinder groups are inspected, + * and the third points to an array that tracks the creation of new + * directories. */ -#define MAXCSBUFS ((128 / sizeof(void *)) - 1) +#define NOCSPTRS ((128 / sizeof(void *)) - 2) /* * A summary of contiguous blocks of various sizes is maintained @@ -142,9 +147,6 @@ * from first cylinder group data blocks. These blocks have to be * read in from fs_csaddr (size fs_cssize) in addition to the * super block. - * - * N.B. sizeof(struct csum) must be a power of two in order for - * the ``fs_cs'' macro to work (see below). */ struct csum { int32_t cs_ndir; /* number of directories */ @@ -188,8 +190,8 @@ struct fs { int32_t fs_fragshift; /* block to frag shift */ int32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ int32_t fs_sbsize; /* actual size of super block */ - int32_t fs_csmask; /* csum block offset */ - int32_t fs_csshift; /* csum block number */ + int32_t fs_csmask; /* csum block offset (now unused) */ + int32_t fs_csshift; /* csum block number (now unused) */ int32_t fs_nindir; /* value of NINDIR */ int32_t fs_inopb; /* value of INOPB */ int32_t fs_nspf; /* value of NSPF */ @@ -225,8 +227,9 @@ struct fs { u_char fs_fsmnt[MAXMNTLEN]; /* name mounted on */ /* these fields retain the current block allocation info */ int32_t fs_cgrotor; /* last cg searched */ - struct csum *fs_csp[MAXCSBUFS];/* list of fs_cs info buffers */ - int32_t *fs_maxcluster; /* max cluster in each cyl group */ + void *fs_ocsp[NOCSPTRS]; /* padding; was list of fs_cs buffers */ + struct csum *fs_csp; /* cg summary info buffer for fs_cs */ + int32_t *fs_maxcluster; /* max cluster in each cyl group */ int32_t fs_cpc; /* cyl per cycle in postbl */ int16_t fs_opostbl[16][8]; /* old rotation block list head */ #if LONG_BIT == 64 @@ -317,11 +320,8 @@ struct fs { /* * Convert cylinder group to base address of its global summary info. - * - * N.B. This macro assumes that sizeof(struct csum) is a power of two. */ -#define fs_cs(fs, indx) \ - fs_csp[(indx) >> (fs)->fs_csshift][(indx) & ~(fs)->fs_csmask] +#define fs_cs(fs, indx) fs_csp[indx] /* * Cylinder group block for a file system. |