summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrigoriy Orlov <gluk@cvs.openbsd.org>2001-04-13 02:39:07 +0000
committerGrigoriy Orlov <gluk@cvs.openbsd.org>2001-04-13 02:39:07 +0000
commit9bff9fb87213262bc5d1afd9333ad540d9e99c54 (patch)
tree5797ca5b1e346d54d4606ff7293258862d4b223b
parent5af2b89b71f9bb495e071539ea9548813c712c7c (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.c8
-rw-r--r--sbin/fsck_ffs/setup.c12
-rw-r--r--sbin/fsck_ffs/utilities.c6
-rw-r--r--sbin/newfs/mkfs.c10
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c25
-rw-r--r--sys/ufs/ffs/fs.h38
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.