[PATCH] FAT Crash in 2.3.99 fix

From: Chris McClellen (chris@transtech.cc)
Date: Thu Mar 30 2000 - 10:35:58 EST


Included is a patch that fixes the BUG()
in fat_get_block whenever one decides to
ftruncate() a file bigger than it was.

Al, could you verify that this patch is not
braindead?

First off, here's some C code that was setting
it off (as posted to the list earlier by
m.luca@iname.com):

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

main(int argc,char **argv)
{
char buf[]="I want to crash fat filesystem";
int fp=open(argv[1], O_RDWR|O_CREAT, 0666);

ftruncate(fp, 1200);

lseek(fp, 1030, SEEK_SET);

write(fp, buf,sizeof(buf));
close(fp);
}

Here's the bug in a nutshell:

ftruncate sets mmu_private to the new size.
It would seem that if mmu_private is not set
to the actual size on _disk_, then
fat_prepare_write (which calls
cont_prepare_write) cannot grow the file to
write to area 1030-1061 of the file, because
it thinks the file is big enough. At some
point vfs realizes block 2 doesnt exist and it
calls fat_get_block to make it.

fat_get_block BUG()s because
it expects mmu_private to be set up correctly.
(A multiple of 512, and in fact should be set
to blocknum*512 of the block being alloc'd;
and fat_get_block can only add blocks to the
end of the last block alloc'd for
the file. So, you cant alloc block 2 until
you have 0 & 1.)

Anyway, what this simple patch does is
not allow ftruncate to increase mmu_private,
only decrease it. mmu_private is passed to
cont_prepare_write btw. This allows
cont_prepare_write to now grow the file before
the write w/o the BUG().

I have used the crash program against the patch
and looked at fat16 and fat32 w/vfat.
I have also copied bunches of big/small files,
and done variations of the theme.

Here's the patch (also included as attachment
in case cut/paste f'd up):

*** linux/fs/fat/file.orig.c Thu Mar 30 10:01:53 2000
--- linux/fs/fat/file.c Thu Mar 30 10:06:20 2000
***************
*** 125,131 ****
 if (IS_IMMUTABLE(inode))
  return /* -EPERM */;
 cluster = SECTOR_SIZE*sbi->cluster_size;
! MSDOS_I(inode)->mmu_private = inode->i_size;
 fat_free(inode,(inode->i_size+(cluster-1))>>sbi->cluster_bits);
 MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
 inode->i_ctime = inode->i_mtime = CURRENT_TIME;
--- 125,137 ----
 if (IS_IMMUTABLE(inode))
  return /* -EPERM */;
 cluster = SECTOR_SIZE*sbi->cluster_size;
!
! /* We never increase mmu_private, or else prepare_write won't
! * be able to grow the file.
! */
! if (MSDOS_I(inode)->mmu_private > inode->i_size)
! MSDOS_I(inode)->mmu_private = inode->i_size;
!
 fat_free(inode,(inode->i_size+(cluster-1))>>sbi->cluster_bits);
 MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
 inode->i_ctime = inode->i_mtime = CURRENT_TIME;



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Mar 31 2000 - 21:00:27 EST