Subject: Re: fsck -c 2 , superblock etc.
To: None <current-users@sun-lamp.cs.berkeley.edu>
From: Wolfgang Solfrank <ws@tools.de>
List: current-users
Date: 06/28/1994 20:50:43
I just had a look at the problem, mostly since I had it happen here, too.

The problem is twofold.

For one thing, fsck tries to convert symbolic links to fastlinks if they are
short enough.

In this process, it tries to read the old symlink value with a request for
its actual length instead of reading a block at a time. But this is a bad
idea on most raw disk devices.

Its a very bad idea on the wd driver, as this driver incorrectly assumes
that requests always come in multiples of 512 bytes. Not only does it read
one block into user memory to get the requested, say 5, bytes (which would
only harm the innocent user program), but it also subtracts 512 from the
request length, resulting in a wrap around of the length, which happens to
be unsigned. Consequently, the driver tries to read further data behind the
mapped user memory, and thus writes into non-existant memory or even overwrites
precious kernel data structures. This results in the panic people are seeing.

Here is a fix for the first problem, including a small change that allows
the use of 'fsck -c 2 -b 32 /dev/xxx' to fix partitions that were corrupted
by this bug.

Can someone please have a look over this, before I check this in?
--
ws@TooLs.DE     (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
--------------- cut --------------- cut --------------- cut ---------------
===================================================================
RCS file: /src/master/NetBSD/sbin/fsck/pass1.c,v
retrieving revision 1.1.1.2
diff -c -r1.1.1.2 pass1.c
*** 1.1.1.2	1994/06/20 14:53:07
--- pass1.c	1994/06/28 14:45:12
***************
*** 96,102 ****
  	struct zlncnt *zlnp;
  	int ndb, j;
  	mode_t mode;
! 	char symbuf[MAXSYMLINKLEN];
  
  	dp = getnextinode(inumber);
  	mode = dp->di_mode & IFMT;
--- 96,102 ----
  	struct zlncnt *zlnp;
  	int ndb, j;
  	mode_t mode;
! 	char *symbuf;
  
  	dp = getnextinode(inumber);
  	mode = dp->di_mode & IFMT;
***************
*** 149,157 ****
  		if (doinglevel2 &&
  		    dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN &&
  		    dp->di_blocks != 0) {
  			if (bread(fsreadfd, symbuf,
  			    fsbtodb(&sblock, dp->di_db[0]),
! 			    (long)dp->di_size) != 0)
  				errexit("cannot read symlink");
  			if (debug) {
  				symbuf[dp->di_size] = 0;
--- 149,158 ----
  		if (doinglevel2 &&
  		    dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN &&
  		    dp->di_blocks != 0) {
+ 			symbuf = alloca(secsize);
  			if (bread(fsreadfd, symbuf,
  			    fsbtodb(&sblock, dp->di_db[0]),
! 			    (long)secsize) != 0)
  				errexit("cannot read symlink");
  			if (debug) {
  				symbuf[dp->di_size] = 0;
===================================================================
RCS file: /src/master/NetBSD/sbin/fsck/setup.c,v
retrieving revision 1.1.1.2
diff -c -r1.1.1.2 setup.c
*** 1.1.1.2	1994/06/20 14:52:53
--- setup.c	1994/06/28 15:29:52
***************
*** 223,229 ****
  		sbdirty();
  		dirty(&asblk);
  	}
! 	if (asblk.b_dirty) {
  		bcopy((char *)&sblock, (char *)&altsblock,
  			(size_t)sblock.fs_sbsize);
  		flush(fswritefd, &asblk);
--- 223,229 ----
  		sbdirty();
  		dirty(&asblk);
  	}
! 	if (asblk.b_dirty && !bflag) {
  		bcopy((char *)&sblock, (char *)&altsblock,
  			(size_t)sblock.fs_sbsize);
  		flush(fswritefd, &asblk);

------------------------------------------------------------------------------