From: hpa Date: Sun, 19 Dec 2004 00:25:14 +0000 (+0000) Subject: Beef up the sanity checking of the boot sector. For really better checking X-Git-Tag: syslinux-3.11~281 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b544013ca57aea35b878e380de19c22ac1abb40e;p=platform%2Fupstream%2Fsyslinux.git Beef up the sanity checking of the boot sector. For really better checking we should be checking the FAT for the media signature, too. --- diff --git a/dos/Makefile b/dos/Makefile index 5b58af0..c423ded 100644 --- a/dos/Makefile +++ b/dos/Makefile @@ -7,6 +7,7 @@ CFLAGS = -W -Wall -ffreestanding -msoft-float $(OPTFLAGS) $(INCLUDES) LDFLAGS = -T com16.ld AR = ar RANLIB = ranlib +LIBGCC := $(shell $(CC) --print-libgcc) SRCS = syslinux.c \ ../syslxmod.c ../bootsect_bin.c ../ldlinux_bin.c \ @@ -33,7 +34,7 @@ spotless: clean installer: syslinux.com syslinux.elf: $(OBJS) libcom.a - $(LD) $(LDFLAGS) -o $@ $^ + $(LD) $(LDFLAGS) -o $@ $^ $(LIBGCC) libcom.a: $(LIBOBJS) -rm -f $@ diff --git a/dos/argv.c b/dos/argv.c index 5535331..4c83589 100644 --- a/dos/argv.c +++ b/dos/argv.c @@ -73,13 +73,13 @@ int __parse_argv(char ***argv, const char *str) if ( ! *p ) break; } - q--; /* Point to final null */ /* Now create argv */ arg = ALIGN_UP(q,char *); *argv = arg; *arg++ = mem; /* argv[0] */ + q--; /* Point q to final null */ for ( r = mem ; r < q ; r++ ) { if ( *r == '\0' ) { *arg++ = r+1; diff --git a/dos/syslinux.c b/dos/syslinux.c index cc611c4..3c58edd 100644 --- a/dos/syslinux.c +++ b/dos/syslinux.c @@ -37,7 +37,7 @@ uint16_t dos_version; void __attribute__((noreturn)) usage(void) { - puts("Usage: syslinux [-sf] drive:\n"); + puts("Usage: syslinux [-sf] drive: [bootsecfile]\n"); exit(1); } @@ -250,7 +250,7 @@ int main(int argc, char *argv[]) libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */ int32_t ldlinux_cluster; int nsectors; - char *device = NULL; + const char *device = NULL, *bootsecfile = NULL; const char *errmsg; int i; @@ -279,8 +279,11 @@ int main(int argc, char *argv[]) opt++; } } else { - if ( device ) + if ( bootsecfile ) usage(); + else if ( device ) + bootsecfile = *argp; + else device = *argp; } } @@ -304,7 +307,7 @@ int main(int argc, char *argv[]) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if(!syslinux_check_bootsect(sectbuf,&errmsg)) { + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { unlock_device(0); puts(errmsg); putchar('\n'); @@ -314,7 +317,7 @@ int main(int argc, char *argv[]) ldlinux_name[0] = dev_fd | 0x40; set_attributes(ldlinux_name, 0); - fd = creat(ldlinux_name, 0x27); /* ARCHIVE SYSTEM HIDDEN READONLY */ + fd = creat(ldlinux_name, 0x07); /* SYSTEM HIDDEN READONLY */ write_file(fd, syslinux_ldlinux, syslinux_ldlinux_len); close(fd); @@ -359,10 +362,15 @@ int main(int argc, char *argv[]) syslinux_make_bootsect(sectbuf); /* Write new boot sector */ - write_device(dev_fd, sectbuf, 1, 0); - - /* Release device lock */ - unlock_device(0); + if ( bootsecfile ) { + unlock_device(0); + fd = creat(bootsecfile, 0x20); /* ARCHIVE */ + write_file(fd, sectbuf, 512); + close(fd); + } else { + write_device(dev_fd, sectbuf, 1, 0); + unlock_device(0); + } /* Done! */ diff --git a/mtools/syslinux.c b/mtools/syslinux.c index 004bca3..20111c6 100644 --- a/mtools/syslinux.c +++ b/mtools/syslinux.c @@ -193,7 +193,7 @@ int main(int argc, char *argv[]) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if( !syslinux_check_bootsect(sectbuf,&errmsg) ) { + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { fprintf(stderr, "%s: %s\n", program, errmsg); exit(1); } diff --git a/syslinux.h b/syslinux.h index dd555a9..ccfb0f0 100644 --- a/syslinux.h +++ b/syslinux.h @@ -31,7 +31,7 @@ void syslinux_make_stupid(void); void syslinux_make_bootsect(void *); /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ -int syslinux_check_bootsect(const void *bs, const char **errmsg); +const char *syslinux_check_bootsect(const void *bs); /* This patches the boot sector and ldlinux.sys based on a sector map */ int syslinux_patch(const uint32_t *sectors, int nsectors); diff --git a/syslxmod.c b/syslxmod.c index 9a1087e..8599625 100644 --- a/syslxmod.c +++ b/syslxmod.c @@ -20,48 +20,74 @@ #include #include #include +#include #include "syslinux.h" #define LDLINUX_MAGIC 0x3eb202fe enum bs_offsets { - bsJump = 0x00, - bsOemName = 0x03, - bsBytesPerSec = 0x0b, - bsSecPerClust = 0x0d, - bsResSectors = 0x0e, - bsFATs = 0x10, - bsRootDirEnts = 0x11, - bsSectors = 0x13, - bsMedia = 0x15, - bsFATsecs = 0x16, - bsSecPerTrack = 0x18, - bsHeads = 0x1a, - bsHiddenSecs = 0x1c, - bsHugeSectors = 0x20, - bsDriveNumber = 0x24, - bsReserved1 = 0x25, - bsBootSignature = 0x26, - bsVolumeID = 0x27, - bsVolumeLabel = 0x2b, - bsFileSysType = 0x36, - bsCode = 0x3e, + bsJump = 0x00, + bsOemName = 0x03, + bsBytesPerSec = 0x0b, + bsSecPerClust = 0x0d, + bsResSectors = 0x0e, + bsFATs = 0x10, + bsRootDirEnts = 0x11, + bsSectors = 0x13, + bsMedia = 0x15, + bsFATsecs = 0x16, + bsSecPerTrack = 0x18, + bsHeads = 0x1a, + bsHiddenSecs = 0x1c, + bsHugeSectors = 0x20, + + /* FAT12/16 only */ + bs16DriveNumber = 0x24, + bs16Reserved1 = 0x25, + bs16BootSignature = 0x26, + bs16VolumeID = 0x27, + bs16VolumeLabel = 0x2b, + bs16FileSysType = 0x36, + bs16Code = 0x3e, + + /* FAT32 only */ + bs32FATSz32 = 36, + bs32ExtFlags = 40, + bs32FSVer = 42, + bs32RootClus = 44, + bs32FSInfo = 48, + bs32BkBootSec = 50, + bs32Reserved = 52, + bs32DriveNumber = 64, + bs32Reserved1 = 65, + bs32BootSignature = 66, + bs32VolumeID = 67, + bs32VolumeLabel = 71, + bs32FileSysType = 82, + bs32Code = 90, + bsSignature = 0x1fe }; #define bsHead bsJump #define bsHeadLen (bsOemName-bsHead) -#define bsCodeLen (bsSignature-bsCode) +#define bsCode bs32Code /* The common safe choice */ +#define bsCodeLen (bsSignature-bs32Code) /* * Access functions for littleendian numbers, possibly misaligned. */ +static inline uint8_t get_8(const unsigned char *p) +{ + return *(const uint8_t *)p; +} + static inline uint16_t get_16(const unsigned char *p) { #if defined(__i386__) || defined(__x86_64__) /* Littleendian and unaligned-capable */ - return *(uint16_t *)p; + return *(const uint16_t *)p; #else return (uint16_t)p[0] + ((uint16_t)p[1] << 8); #endif @@ -71,7 +97,7 @@ static inline uint32_t get_32(const unsigned char *p) { #if defined(__i386__) || defined(__x86_64__) /* Littleendian and unaligned-capable */ - return *(uint32_t *)p; + return *(const uint32_t *)p; #else return (uint32_t)p[0] + ((uint32_t)p[1] << 8) + ((uint32_t)p[2] << 16) + ((uint32_t)p[3] << 24); @@ -119,65 +145,87 @@ void syslinux_make_bootsect(void *bs) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock; - * Return 0 if bad and 1 if OK. + * Return NULL if OK and otherwise an error message; */ -int syslinux_check_bootsect(const void *bs, const char **errmsg) +const char *syslinux_check_bootsect(const void *bs) { int veryold; - unsigned int sectors, clusters; + int sectorsize; + long long sectors, fatsectors, dsectors; + long long clusters; + int rootdirents, clustersize; const unsigned char *sectbuf = bs; - *errmsg = 0; - - /*** FIX: Handle FAT32 ***/ + veryold = 0; - if ( sectbuf[bsBootSignature] == 0x29 ) { - /* It's DOS, and it has all the new nice fields */ - - veryold = 0; - - sectors = get_16(sectbuf+bsSectors); - sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors); - clusters = sectors / sectbuf[bsSecPerClust]; - - if ( !memcmp(sectbuf+bsFileSysType, "FAT12 ", 8) ) { - if ( clusters > 4086 ) { - *errmsg = "FAT12 but claims more than 4086 clusters"; - return 0; - } - } else if ( !memcmp(sectbuf+bsFileSysType, "FAT16 ", 8) ) { - if ( clusters <= 4086 ) { - *errmsg = "FAT16 but claims less than 4086 clusters"; - return 0; + /* Must be 0xF0 or 0xF8..0xFF */ + if ( get_8(sectbuf+bsMedia) != 0xF0 && + get_8(sectbuf+bsMedia) < 0xF8 ) + goto invalid; + + sectorsize = get_16(sectbuf+bsBytesPerSec); + if ( sectorsize == 512 ) + ; /* ok */ + else if ( sectorsize == 1024 || sectorsize == 2048 || sectorsize == 4096 ) + return "only 512-byte sectors are supported"; + else + goto invalid; + + clustersize = get_8(sectbuf+bsSecPerClust); + if ( clustersize == 0 || (clustersize & (clustersize-1)) ) + goto invalid; /* Must be nonzero and a power of 2 */ + + sectors = get_16(sectbuf+bsSectors); + sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors); + + dsectors = sectors - get_16(sectbuf+bsResSectors); + + fatsectors = get_16(sectbuf+bsFATsecs); + fatsectors = fatsectors ? fatsectors : get_32(sectbuf+bs32FATSz32); + fatsectors *= get_8(sectbuf+bsFATs); + dsectors -= fatsectors; + + rootdirents = get_16(sectbuf+bsRootDirEnts); + dsectors -= (rootdirents+sectorsize/32-1)/sectorsize; + + if ( dsectors < 0 || fatsectors == 0 ) + goto invalid; + + clusters = dsectors/clustersize; + + if ( clusters < 0xFFF5 ) { + /* FAT12 or FAT16 */ + + if ( !get_16(sectbuf+bsFATsecs) ) + goto invalid; + + if ( get_8(sectbuf+bs16BootSignature) == 0x29 ) { + if ( !memcmp(sectbuf+bs16FileSysType, "FAT12 ", 8) ) { + if ( clusters >= 4085 ) + return "more than 4084 clusters but claims FAT12"; + } else if ( !memcmp(sectbuf+bs16FileSysType, "FAT16 ", 8) ) { + if ( clusters < 4085 ) + return "less than 4084 clusters but claims FAT16"; + } else if ( memcmp(sectbuf+bs16FileSysType, "FAT ", 8) ) { + static char fserr[] = "filesystem type \"????????\" not supported"; + memcpy(fserr+17, sectbuf+bs16FileSysType, 8); + return fserr; } - } else if ( !memcmp(sectbuf+bsFileSysType, "FAT ", 8) ) { - /* OS/2 sets up the filesystem as just `FAT'. */ - } else { - static char fserr[] = "filesystem type \"????????\" not supported"; - memcpy(fserr+17, sectbuf+bsFileSysType, 8); - *errmsg = fserr; - return 0; } + } else if ( clusters < 0x0FFFFFF5 ) { + /* FAT32 */ + /* Moving the FileSysType and BootSignature was a lovely stroke of M$ idiocy */ + if ( get_8(sectbuf+bs32BootSignature) != 0x29 || + !memcmp(sectbuf+bs32FileSysType, "FAT32 ", 8) ) + goto invalid; } else { - veryold = 1; - - if ( sectbuf[bsSecPerClust] & (sectbuf[bsSecPerClust] - 1) || - sectbuf[bsSecPerClust] == 0 ) { - *errmsg = "this doesn't look like a FAT filesystem"; - return 0; - } - - sectors = get_16(sectbuf+bsSectors); - sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors); - clusters = sectors / sectbuf[bsSecPerClust]; - } - - if ( get_16(sectbuf+bsBytesPerSec) != 512 ) { - *errmsg = "sector sizes other than 512 not supported"; - return 0; + goto invalid; } + + return NULL; - return 1; + invalid: + return "this doesn't look like a valid FAT filesystem"; } /* @@ -231,6 +279,6 @@ int syslinux_patch(const uint32_t *sectors, int nsectors) set_32(patcharea+4, csum); - return 0; + return 0; } diff --git a/unix/syslinux.c b/unix/syslinux.c index 362fcc3..60d114f 100644 --- a/unix/syslinux.c +++ b/unix/syslinux.c @@ -240,7 +240,7 @@ int main(int argc, char *argv[]) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if(!syslinux_check_bootsect(sectbuf,&errmsg)) { + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { fprintf(stderr, "%s: %s\n", device, errmsg); exit(1); } diff --git a/win32/syslinux.c b/win32/syslinux.c index 2f614b4..758b73f 100644 --- a/win32/syslinux.c +++ b/win32/syslinux.c @@ -170,7 +170,7 @@ int main(int argc, char *argv[]) } /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if(!syslinux_check_bootsect(sectbuf,&errmsg)) { + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { fprintf(stderr, "%s\n", errmsg); exit(1); }