Subject: kern/28134: invalid signed left-shifts in ffs_vfsops.c
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <dholland@eecs.harvard.edu>
List: netbsd-bugs
Date: 11/09/2004 11:05:54
>Number:         28134
>Category:       kern
>Synopsis:       invalid signed left-shifts in ffs_vfsops.c
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Nov 09 11:05:54 +0000 2004
>Originator:     David A. Holland <dholland@eecs.harvard.edu>
>Release:        NetBSD 2.0G (actually -20041029)
>Organization:
   Harvard EECS
>Environment:
	
	
System: NetBSD alicante 2.0G NetBSD 2.0G (ALICANTE) #9: Fri Aug 27 17:49:09 EDT 2004 dholland@alicante:/usr/src/sys/arch/i386/compile/ALICANTE i386
Architecture: i386
Machine: i386
>Description:
	In ffs_mount() the "please fsck(8)" message includes a
	printout of the value of ->fs_clean. It's printed with %x;
	however, fs_clean is int8_t. Thus, if the high bit gets set
	the printed value is sign-extended. This is merely untidy.

	However, the way the high bit gets set is via the left shifts
	in ffs_mount() and ffs_mountfs(). Shifting into the sign bit
	is formally undefined and a Bad Thing.

	As best I can tell the best solution is to make fs_clean
	unsigned (uint8_t); it's only used in a few places. 

	However, those places include fsck and clri and other userland
	tools. And the on-disk superblock. So I'm not sure if such a
	change is really a good idea, even though it won't break
	binary compatibility. So I'm just filing the PR and making
	someone else decide. :-)

	This discussion (and the patches below) apply to version
	1.44 of sys/ufs/ffs/fs.h and version 1.155 of
	sys/ufs/ffs/ffs_vfsops.c, both of which are still current as
	of this writing.

	I also looked at

		sbin/clri/clri.c		1.18
		sbin/fsck_ffs/setup.c		1.73
		sbin/fsck_ffs/utilities.c	1.47
		sbin/fsdb/fsdb.c		1.29
		sbin/newfs/mkfs.c		1.88
		usr.sbin/dumpfs/dumpfs.c	1.47
		usr.sbin/makefs/ffs/mkfs.c	1.20

	but only with grep so it's conceivable I missed something.
	These are from -20041029, even though my running kernel is
	older.

>How-To-Repeat:

	If I understand correctly, produce an unclean filesystem that
	"was clean" and mount it a few more times without fscking; you
	get

	   /dev/wd0k: file system not clean (fs_clean=4); please fsck(8)
	   /dev/wd0k: lost blocks 0 files 0
	   /dev/wd0k: file system not clean (fs_clean=8); please fsck(8)
	   /dev/wd0k: lost blocks 0 files 0
	   /dev/wd0k: file system not clean (fs_clean=10); please fsck(8)
	   /dev/wd0k: lost blocks 0 files 0
	   /dev/wd0k: file system not clean (fs_clean=20); please fsck(8)
	   /dev/wd0k: lost blocks 0 files 0
	   /dev/wd0k: file system not clean (fs_clean=40); please fsck(8)
	   /dev/wd0k: lost blocks 0 files 0
	   /dev/wd0k: file system not clean (fs_clean=ffffff80); please fsck(8)
	   /dev/wd0k: lost blocks 0 files 0
	   /dev/wd0k: file system not clean (fs_clean=0); please fsck(8)
	   /dev/wd0k: lost blocks 0 files 0

	(This was observed by someone other than me, so yes it can
	really happen but I'm not absolutely certain of the mechanism.)

>Fix:

This patch makes fs_clean unsigned:

Index: fs.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/fs.h,v
retrieving revision 1.44
diff -u -r1.44 fs.h
--- fs.h	25 May 2004 14:54:59 -0000	1.44
+++ fs.h	9 Nov 2004 09:23:05 -0000
@@ -310,7 +310,7 @@
 	struct	csum fs_old_cstotal;	/* cylinder summary information */
 /* these fields are cleared at mount time */
 	int8_t	 fs_fmod;		/* super block modified flag */
-	int8_t	 fs_clean;		/* file system is clean flag */
+	uint8_t	 fs_clean;		/* file system is clean flag */
 	int8_t	 fs_ronly;		/* mounted read-only flag */
 	uint8_t	 fs_old_flags;		/* see FS_ flags below */
 	u_char	 fs_fsmnt[MAXMNTLEN];	/* name mounted on */


I haven't actually tested this patch, but I've applied it to my tree,
so since I've marked this PR low priority I probably will have by the
time anyone looks at it. :-)

I don't see anything (in the kernel or in userland either) that
requires any adjustment to match. It's printed with %d in
sbin/fsck_ffs/setup.c, but that's harmless.


However, if that change is no good, this patch suppresses the
sign-extended printout:

Index: ffs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vfsops.c,v
retrieving revision 1.155
diff -u -r1.155 ffs_vfsops.c
--- ffs_vfsops.c	21 Sep 2004 03:10:36 -0000	1.155
+++ ffs_vfsops.c	9 Nov 2004 08:28:47 -0000
@@ -428,7 +428,8 @@
 			fs->fs_time = time.tv_sec;
 		else {
 			printf("%s: file system not clean (fs_clean=%x); please fsck(8)\n",
-			    mp->mnt_stat.f_mntfromname, fs->fs_clean);
+			    mp->mnt_stat.f_mntfromname, 
+			    (unsigned)(u_int8_t)fs->fs_clean);
 			printf("%s: lost blocks %" PRId64 " files %d\n",
 			    mp->mnt_stat.f_mntfromname, fs->fs_pendingblocks,
 			    fs->fs_pendinginodes);


I haven't, however, written a patch to avoid the undefined shifts
without making fs_clean unsigned.

>Unformatted: