Subject: kern/4641: LFS checksum calculated incorrectly
To: None <gnats-bugs@gnats.netbsd.org, perseant@hitl.washington.edu>
From: None <perseant@hitl.washington.edu>
List: netbsd-bugs
Date: 12/04/1997 18:27:25
>Number:         4641
>Category:       kern
>Synopsis:       LFS on-disk checksum includes non-recoverable data
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Dec  4 18:35:01 1997
>Last-Modified:
>Originator:     Konrad Schroder
>Organization:
Konrad Schroder           http://www.hitl.washington.edu/people/perseant/
System Administrator                         perseant@hitl.washington.edu
Human Interface Technology Lab                      Voice: (206) 616-1478
Box 352142, University of Washington, 98195, USA      FAX: (206) 543-5380
>Release:        Thu Dec  4 18:08:08 PST 1997
>Environment:
	
System: NetBSD inle 1.2G NetBSD 1.2G (INLE) #8: Thu Dec 4 17:54:39 PST 1997 perseant@inle:/usr/src/sys/arch/i386/compile/INLE i386


>Description:
It would appear that the checksum put into the LFS superblocks assumes
that lfs_cksum is the last field in struct lfs...which is not, or at least
no longer, the case.  Since this checksum is never used for anything
in the LFS code, this is not a problem for normal operation; however, it
makes it impossible to build a tool that analyzes the LFS for sanity
(e.g., an fsck_lfs that does more than just a roll forward).
>How-To-Repeat:
Here's a program (with pieces liberally stolen from fsck_ffs and ufs/lfs)
that will complain about a bad checksum.  If compiled with -DSANE, its
check will match the patch below, otherwise it will use the original
checksum method.

To use:
   # newlfs -L /dev/rsd1a    (or use one you already have, no difference)
   # ./cksum_lfs /dev/rsd1a
--------8<--------
/*
 * cksum_lfs.c - carved out of bits and pieces from an in-process fsck_lfs.
 * 
 * Copyright (c) 1997
 *  Konrad Schroder.  All rights reserved.
 * 
 * Copyright (c) 1980, 1986, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/time.h>
#include <ufs/ufs/dinode.h>
#include <sys/mount.h> /* XXX ufs/lfs/lfs.h should include this for us */
#include <ufs/lfs/lfs.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/file.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int fsreadfd;
long dev_bsize;
#define secsize dev_bsize
#define rwerror(msg, blk) fprintf(stderr, "%s ERROR AT BLK %lu", (msg), (blk))

/* from lfs_cksum.c */
u_long
cksum(void *str, size_t len)
{
    register u_long sum;
        
    len &= ~(sizeof(u_short) - 1);
    for (sum = 0; len; len -= sizeof(u_short)) {
        sum ^= *(u_short *)str;
        str = (void *)((u_short *)str + 1);
    }
    return (sum);
}

/* from utilities.c */
int
bread(int fd, char *buf, daddr_t blk, long size)
{
    char *cp;
    int i, errs;
    off_t offset;
    
    offset = blk;
    offset *= dev_bsize;
    if (lseek(fd, offset, 0) < 0)
        rwerror("SEEK", blk);
    else if (read(fd, buf, (int)size) == size)
        return (0);
    rwerror("READ", blk);
    if (lseek(fd, offset, 0) < 0)
        rwerror("SEEK", blk);
    errs = 0;
    memset(buf, 0, (size_t)size);
    printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
    for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
        if (read(fd, cp, (int)secsize) != secsize) {
            (void)lseek(fd, offset + i + secsize, 0);
            if (secsize != dev_bsize && dev_bsize != 1)
                printf(" %ld (%ld),",
                       (blk * dev_bsize + i) / secsize,
                       blk + i / dev_bsize);
            else
                printf(" %ld,", blk + i / dev_bsize);
            errs++;
        }
    }
    printf("\n");
    return (errs);
}

static struct disklabel *
getdisklabel(int fd)
{
    static struct disklabel lab;
    
    if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
        return NULL;
    }
    return (&lab);
}

/*
 * Read in the LFS super block and its summary info.
 */
void readsb(int fsreadfd)
{
    daddr_t super;
    u_int32_t checksum;
    char sblock_char[LFS_SBPAD];
    struct lfs *sblock = (struct lfs *)sblock_char;
    struct disklabel *lp;

    /* get the device block size */
    if ((lp = getdisklabel(fsreadfd)) != NULL)
        dev_bsize = lp->d_secsize;
    else
        dev_bsize = DEV_BSIZE;

    /* read in the superblock */
    super = LFS_LABELPAD / dev_bsize;    
    if (bread(fsreadfd, (char *)sblock, super, (long)LFS_SBPAD) != 0) {
        perror("Couldn't read superblock");
        return;
    }

    /* checksum */
#ifdef SANE
    /* a sane way */
    checksum = cksum(sblock, (size_t)(&((struct lfs *)0)->lfs_cksum));
#else
    /* the 4.4BSD way (checksums the checksum too!) */
    checksum = cksum(sblock, sizeof(struct lfs) - sizeof(sblock->lfs_cksum));
#endif
    if(sblock->lfs_cksum != checksum)
    {
        printf("Superblock checksum (%lu) does not match computed checksum %lu\n",
               sblock->lfs_cksum, checksum);
    }

    /* that's all folks */
    return;
}

int main(int argc, char **argv)
{
    if(argv[1]==NULL)
    {
        fprintf(stderr,"usage: lfs_cksum <dev>\n");
        exit(1);
    }
    fsreadfd = open(argv[1],O_RDONLY,0);
    if(fsreadfd<0)
        perror(argv[1]);
    else
        readsb(fsreadfd);

    return 0;
}
--------8<--------
>Fix:
Here's one solution, a patch to ufs/lfs/lfs_segment.c, computing the
checksum based on offsetof(struct lfs, lfs_cksum):

*** lfs_segment.c.dist  Thu Dec  4 15:54:37 1997
--- lfs_segment.c       Thu Dec  4 18:04:03 1997
***************
*** 930,934 ****
  
        /* Checksum the superblock and copy it into a buffer. */
!       fs->lfs_cksum = cksum(fs, sizeof(struct lfs) - sizeof(fs->lfs_cksum));
        bp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp,
        fs->lfs_sboffs[0],
            LFS_SBPAD);
--- 930,934 ----
  
        /* Checksum the superblock and copy it into a buffer. */
!       fs->lfs_cksum = cksum(fs, (size_t)(&((struct lfs *)0)->lfs_cksum));
        bp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp,
        fs->lfs_sboffs[0],
            LFS_SBPAD);

>Audit-Trail:
>Unformatted: