Patch for kernel oops in fs/fat/inode.c

Richard Watts (Richard.Watts@cl.cam.ac.uk)
Tue, 29 Sep 1998 18:17:49 +0100


Some NTFS partitions have what would be the fat_length field in the
boot block set to 0. If you attempt to mount one as one of the FAT
filesystem types (msdos, vfat, umsdos), this tricks fat_read_super()
in fs/fat/inode.c into believing them to be FAT32 partitions. It then
dutifully examines info_sect, and toddles off to (info_sect
*sector_size) + 0x1e0 to find the file system information sector.

There are two problems with this:

1. The info_sect field in NTFS doesn't contain a valid sector.
2. fat_read_super() assumes the information sector is in the first
1024 bytes of the partition (ie. it looks in the 1024 bytes it's already
read). This causes a kernel oops.

The appended patch (against a clean 2.1.123) fixes this, first by
(loosely) checking whether the information sector is on the disk
(ie. whether info_sect < total_sect: FAT32 appears to honour
total_sect, at least on my box), and secondly by checking whether
accessing the information sector would cause an access beyond the end
of the 1024 bytes available, and bailing out if so.

The first of these checks is a bit questionable, but the second is
a simple `fail nicely or segfault' check which should probably be
in the kernel somewhere.

I don't have any machines (apart from my own) to test this on, so
I'd be grateful for any feedback - especially from those with FAT32
partitions that might be unreasonably rejected, and those who actually
know something about how to submit kernel patches, since I've almost
certainly done something(s) wrong.

This patch is (lightly) tested against 2.1.122: should work against
.123, but I haven't been able to get .123 to compile yet.

Richard.
-------- snip snip snip ---------------
diff -cr clean-2.1.123/fs/fat/inode.c current/fs/fat/inode.c
*** clean-2.1.123/fs/fat/inode.c Thu Sep 10 22:32:26 1998
--- current/fs/fat/inode.c Tue Sep 29 17:24:13 1998
***************
*** 277,283 ****
struct fat_boot_sector *b;
char *p;
int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
! int debug,error,fat,cp;
int blksize = 512;
int fat32;
struct fat_mount_options opts;
--- 277,283 ----
struct fat_boot_sector *b;
char *p;
int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
! int debug,error=0,fat,cp;
int blksize = 512;
int fat32;
struct fat_mount_options opts;
***************
*** 345,350 ****
--- 345,356 ----
* (by Drew Eckhardt)
*/

+ /* If the FAT32 information sector is at > 1024, we deduce that it's
+ going to be past the end of the superblock, and bomb out, since
+ an attempt to access past the end of the superblock will cause
+ a kernel oops. This is related to the block size given in bread()
+ above. */
+ #define MAX_SANE_FSINFO_SIZE 1024
#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
/* don't divide by zero */

***************
*** 362,381 ****
fat32 = 1;
MSDOS_SB(sb)->fat_length= CF_LE_W(b->fat32_length)*sector_mult;
MSDOS_SB(sb)->root_cluster = CF_LE_L(b->root_cluster);
! MSDOS_SB(sb)->fsinfo_offset =
! CF_LE_W(b->info_sector) * logical_sector_size + 0x1e0;
! fsinfo = (struct fat_boot_fsinfo *)
! &bh->b_data[MSDOS_SB(sb)->fsinfo_offset];
! if (CF_LE_L(fsinfo->signature) != 0x61417272) {
! printk("fat_read_super: Did not find valid FSINFO "
! "signature. Found 0x%x\n",
! CF_LE_L(fsinfo->signature));
! } else {
! MSDOS_SB(sb)->free_clusters = CF_LE_L(fsinfo->free_clusters);
}
} else {
! fat32 = 0;
! MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult;
MSDOS_SB(sb)->root_cluster = 0;
MSDOS_SB(sb)->free_clusters = -1; /* Don't know yet */
}
--- 370,410 ----
fat32 = 1;
MSDOS_SB(sb)->fat_length= CF_LE_W(b->fat32_length)*sector_mult;
MSDOS_SB(sb)->root_cluster = CF_LE_L(b->root_cluster);
! {
! unsigned long infosec = CF_LE_W(b->info_sector);
!
! if (infosec > CF_LE_L(b->total_sect)) {
! printk("FAT32 information sector is past end of "
! "filesystem (%lx > %x)- NTFS partition ?\n",
! infosec, CF_LE_L(b->total_sect));
! error = 1;
! }
! }
! if (!error) {
! MSDOS_SB(sb)->fsinfo_offset =
! CF_LE_W(b->info_sector) * logical_sector_size + 0x1e0;
! if (MSDOS_SB(sb)->fsinfo_offset > MAX_SANE_FSINFO_SIZE) {
! error = 1;
! printk("FAT32 information sector is past end of "
! "superblock (%lx > SANE_FSINFO_SIZE (%x)). "
! " NTFS partition ?",
! MSDOS_SB(sb)->fsinfo_offset,
! MAX_SANE_FSINFO_SIZE);
! } else {
! fsinfo = (struct fat_boot_fsinfo *)
! &bh->b_data[MSDOS_SB(sb)->fsinfo_offset];
! if (CF_LE_L(fsinfo->signature) != 0x61417272) {
! printk("fat_read_super: Did not find valid FSINFO "
! "signature. Found 0x%x\n",
! CF_LE_L(fsinfo->signature));
! } else {
! MSDOS_SB(sb)->free_clusters = CF_LE_L(fsinfo->free_clusters);
! }
! }
}
} else {
! fat32 = 0;
! MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult;
MSDOS_SB(sb)->root_cluster = 0;
MSDOS_SB(sb)->free_clusters = -1; /* Don't know yet */
}
***************
*** 391,397 ****
data_sectors = CF_LE_L(b->total_sect);
}
data_sectors = data_sectors * sector_mult - MSDOS_SB(sb)->data_start;
! error = !b->cluster_size || !sector_mult;
if (!error) {
MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
b->cluster_size/sector_mult : 0;
--- 420,426 ----
data_sectors = CF_LE_L(b->total_sect);
}
data_sectors = data_sectors * sector_mult - MSDOS_SB(sb)->data_start;
! error = error || !b->cluster_size || !sector_mult;
if (!error) {
MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
b->cluster_size/sector_mult : 0;
***************
*** 419,426 ****
i = -1;
else
i = detect_cvf(sb,cvf_format);
! if (i >= 0)
! error = cvf_formats[i]->mount_cvf(sb,cvf_options);
if (error || debug) {
/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
--- 448,455 ----
i = -1;
else
i = detect_cvf(sb,cvf_format);
! if (!error && i >= 0)
! error = cvf_formats[i]->mount_cvf(sb,cvf_options);
if (error || debug) {
/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
----------- snip snip snip ---------

-
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/