From 55865a05d3729f6f7245ba60146b12bd5048496a Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 27 Apr 2004 06:48:59 +0000 Subject: [PATCH] Handle memdisk images compressed with zip as well as with gzip. --- NEWS | 6 ++ memdisk/inflate.c | 88 +------------------------- memdisk/init.S16 | 4 +- memdisk/memdisk.h | 6 +- memdisk/setup.c | 16 ++++- memdisk/unzip.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 208 insertions(+), 98 deletions(-) diff --git a/NEWS b/NEWS index 3f1682b..a89b153 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,12 @@ Starting with 1.47, changes marked with SYSLINUX/PXELINUX/ISOLINUX apply to that specific program only; other changes apply to all of them. +Changes in 2.10: + * MEMDISK: Handle images compressed with zip as well as with + gzip. Some Windows-based image tools apparently generate + these kinds of images by default. Patch by Patrick + LoPresti. + Changes in 2.09: * SYSLINUX: Remove residual setuid crap from syslinux-nomtools. diff --git a/memdisk/inflate.c b/memdisk/inflate.c index 9d701d6..2974970 100644 --- a/memdisk/inflate.c +++ b/memdisk/inflate.c @@ -1055,71 +1055,10 @@ makecrc(void) /* * Do the uncompression! */ -int gunzip(void) +int gunzip() { - uch flags; - unsigned char magic[2]; /* magic header */ - char method; - ulg orig_crc = 0; /* original crc */ - ulg orig_len = 0; /* original uncompressed length */ int res; - magic[0] = (unsigned char)get_byte(); - magic[1] = (unsigned char)get_byte(); - method = (unsigned char)get_byte(); - - if (magic[0] != 037 || - ((magic[1] != 0213) && (magic[1] != 0236))) { - error("bad gzip magic numbers"); - return -1; - } - - /* We only support method #8, DEFLATED */ - if (method != 8) { - error("internal error, invalid method"); - return -1; - } - - flags = (uch)get_byte(); - if ((flags & ENCRYPTED) != 0) { - error("Input is encrypted"); - return -1; - } - if ((flags & CONTINUATION) != 0) { - error("Multi part input"); - return -1; - } - if ((flags & RESERVED) != 0) { - error("Input has invalid flags"); - return -1; - } - - /* Ignore timestamp */ - (void)get_byte(); - (void)get_byte(); - (void)get_byte(); - (void)get_byte(); - - (void)get_byte(); /* Ignore extra flags for the moment */ - (void)get_byte(); /* Ignore OS type for the moment */ - - if ((flags & EXTRA_FIELD) != 0) { - unsigned len = (unsigned)get_byte(); - len |= ((unsigned)get_byte())<<8; - while (len--) (void)get_byte(); - } - - /* Get original file name if it was truncated */ - if ((flags & ORIG_NAME) != 0) { - /* Discard the old name */ - while (get_byte() != 0) /* null */ ; - } - - /* Discard file comment if any */ - if ((flags & COMMENT) != 0) { - while (get_byte() != 0) /* null */ ; - } - /* Decompress */ if ((res = inflate())) { switch (res) { @@ -1139,29 +1078,6 @@ int gunzip(void) } return -1; } - - /* Get the crc and original length */ - /* crc32 (see algorithm.doc) - * uncompressed input size modulo 2^32 - */ - orig_crc = (ulg) get_byte(); - orig_crc |= (ulg) get_byte() << 8; - orig_crc |= (ulg) get_byte() << 16; - orig_crc |= (ulg) get_byte() << 24; - - orig_len = (ulg) get_byte(); - orig_len |= (ulg) get_byte() << 8; - orig_len |= (ulg) get_byte() << 16; - orig_len |= (ulg) get_byte() << 24; - - /* Validate decompression */ - if (orig_crc != CRC_VALUE) { - error("crc error"); - return -1; - } - if (orig_len != bytes_out) { - error("length error"); - return -1; - } + return 0; } diff --git a/memdisk/init.S16 b/memdisk/init.S16 index a70f08d..0a4a330 100644 --- a/memdisk/init.S16 +++ b/memdisk/init.S16 @@ -42,7 +42,7 @@ boot_flag: .word 0xAA55 _start: jmp start -# This is the setup header, and it must start at %cs:2 (old 0x9020:2) + # This is the setup header, and it must start at %cs:2 (old 0x9020:2) .ascii "HdrS" # header signature .word 0x0203 # header version number (>= 0x0105) @@ -57,7 +57,7 @@ type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, # See Documentation/i386/boot.txt for # assigned ids -# flags, unused bits must be zero (RFU) bit within loadflags + # flags, unused bits must be zero (RFU) bit within loadflags loadflags: LOADED_HIGH = 1 # If set, the kernel is loaded high CAN_USE_HEAP = 0x80 # If set, the loader also has set diff --git a/memdisk/memdisk.h b/memdisk/memdisk.h index ace53b0..5377120 100644 --- a/memdisk/memdisk.h +++ b/memdisk/memdisk.h @@ -47,6 +47,10 @@ memcpy_endptr(void *__d, const void *__s, unsigned int __n) } /* Decompression */ -void *unzip(void *indata, unsigned long zbytes, void *target); +extern int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p, + uint32_t *dbytes_p, uint32_t *orig_crc, + uint32_t *offset_p); +extern void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes, + uint32_t orig_crc, void *target); #endif diff --git a/memdisk/setup.c b/memdisk/setup.c index aff0548..b3bcc9d 100644 --- a/memdisk/setup.c +++ b/memdisk/setup.c @@ -232,14 +232,23 @@ void unzip_if_needed(uint32_t *where_p, uint32_t *size_p) { uint32_t where = *where_p; uint32_t size = *size_p; + uint32_t zbytes; uint32_t startrange, endrange; uint32_t gzdatasize, gzwhere; + uint32_t orig_crc, offset; uint32_t target = 0; int i, okmem; /* Is it a gzip image? */ - if ( *(uint16_t *)where == 0x8b1f ) { - gzdatasize = *(uint32_t *)(where + size - 4); + if (check_zip ((void *)where, size, &zbytes, &gzdatasize, + &orig_crc, &offset) == 0) { + + if (offset + zbytes > size) { + /* Assertion failure; check_zip is supposed to guarantee this + never happens. */ + puts("internal error: check_zip returned nonsense\n"); + die(); + } /* Find a good place to put it: search memory ranges in descending order until we find one that is legal and fits */ @@ -312,7 +321,8 @@ void unzip_if_needed(uint32_t *where_p, uint32_t *size_p) target, gzdatasize); *size_p = gzdatasize; - *where_p = (uint32_t)unzip((void *)where, size, (void *)target); + *where_p = (uint32_t)unzip((void *)(where + offset), zbytes, + gzdatasize, orig_crc, (void *)target); } } diff --git a/memdisk/unzip.c b/memdisk/unzip.c index de49e2a..b7d2fdd 100644 --- a/memdisk/unzip.c +++ b/memdisk/unzip.c @@ -177,6 +177,171 @@ static void error(char *x) die(); } +/* GZIP header */ +struct gzip_header { + uint16_t magic; + uint8_t method; + uint8_t flags; + uint32_t timestamp; + uint8_t extra_flags; + uint8_t os_type; +} __attribute__ ((packed)); +/* (followed by optional and variable length "extra", "original name", + and "comment" fields) */ + +struct gzip_trailer { + uint32_t crc; + uint32_t dbytes; +} __attribute__ ((packed)); + +/* PKZIP header. See + * . + */ +struct pkzip_header { + uint32_t magic; + uint16_t version; + uint16_t flags; + uint16_t method; + uint16_t modified_time; + uint16_t modified_date; + uint32_t crc; + uint32_t zbytes; + uint32_t dbytes; + uint16_t filename_len; + uint16_t extra_len; +} __attribute__ ((packed)); +/* (followed by optional and variable length "filename" and "extra" + fields) */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* pkzip flag byte */ +#define PK_ENCRYPTED 0x01 /* bit 0 set: file is encrypted */ +#define PK_DATADESC 0x08 /* bit 3 set: file has trailing "data + descriptor" */ +#define PK_UNSUPPORTED 0xFFF0 /* All other bits must be zero */ + + +/* Return 0 if (indata, size) points to a ZIP file, and fill in + compressed data size, uncompressed data size, CRC, and offset of + data. + + If indata is not a ZIP file, return -1. */ +int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p, + uint32_t *dbytes_p, uint32_t *orig_crc, uint32_t *offset_p) { + struct gzip_header *gzh = (struct gzip_header *)indata; + struct pkzip_header *pkzh = (struct pkzip_header *)indata; + uint32_t offset; + + if (gzh->magic == 0x8b1f) { + struct gzip_trailer *gzt = indata + size - sizeof (struct gzip_trailer); + /* We only support method #8, DEFLATED */ + if (gzh->method != 8) { + error("gzip file uses invalid method"); + return -1; + } + if (gzh->flags & ENCRYPTED) { + error("gzip file is encrypted; not supported"); + return -1; + } + if (gzh->flags & CONTINUATION) { + error("gzip file is a continuation file; not supported"); + return -1; + } + if (gzh->flags & RESERVED) { + error("gzip file has unsupported flags"); + return -1; + } + offset = sizeof (*gzh); + if (gzh->flags & EXTRA_FIELD) { + /* Skip extra field */ + unsigned len = *(unsigned *)(indata + offset); + offset += 2 + len; + } + if (gzh->flags & ORIG_NAME) { + /* Discard the old name */ + uint8_t *p = indata; + while (p[offset] != 0 && offset < size) { + offset++; + } + offset++; + } + + if (gzh->flags & COMMENT) { + /* Discard the comment */ + uint8_t *p = indata; + while (p[offset] != 0 && offset < size) { + offset++; + } + offset++; + } + + if (offset > size) { + error ("gzip file corrupt"); + return -1; + } + *zbytes_p = size - offset - sizeof (struct gzip_trailer); + *dbytes_p = gzt->dbytes; + *orig_crc = gzt->crc; + *offset_p = offset; + return 0; + } + else if (pkzh->magic == 0x04034b50UL) { + /* Magic number matches pkzip file. */ + + offset = sizeof (*pkzh); + if (pkzh->flags & PK_ENCRYPTED) { + error("pkzip file is encrypted; not supported"); + return -1; + } + if (pkzh->flags & PK_DATADESC) { + error("pkzip file uses data_descriptor field; not supported"); + return -1; + } + if (pkzh->flags & PK_UNSUPPORTED) { + error("pkzip file has unsupported flags"); + return -1; + } + + /* We only support method #8, DEFLATED */ + if (pkzh->method != 8) { + error("pkzip file uses invalid method"); + return -1; + } + /* skip header */ + offset = sizeof (*pkzh); + /* skip filename */ + offset += pkzh->filename_len; + /* skip extra field */ + offset += pkzh->extra_len; + + if (offset + pkzh->zbytes > size) { + error ("pkzip file corrupt"); + return -1; + } + + *zbytes_p = pkzh->zbytes; + *dbytes_p = pkzh->dbytes; + *orig_crc = pkzh->crc; + *offset_p = offset; + return 0; + } + else { + /* Magic number does not match. */ + return -1; + } + + error ("Internal error in check_zip"); + return -1; +} + /* * Decompress the image, trying to flush the end of it as close * to end_mem as possible. Return a pointer to the data block, @@ -184,18 +349,19 @@ static void error(char *x) */ extern void _end; -void *unzip(void *indata, unsigned long zbytes, void *target) +void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes, + uint32_t orig_crc, void *target) { - /* The uncompressed length of a gzip file is the last four bytes */ - unsigned long dbytes = *(uint32_t *)((char *)indata + zbytes - 4); - /* Set up the heap; it's the 64K after the bounce buffer */ free_mem_ptr = (ulg)sys_bounce + 0x10000; free_mem_end_ptr = free_mem_ptr + 0x10000; /* Set up input buffer */ inbuf = indata; - insize = inbytes = zbytes; + /* Sometimes inflate() looks beyond the end of the compressed data, + but it always backs up before it is done. So we give it 4 bytes + of slack. */ + insize = inbytes = zbytes + 4; /* Set up output buffer */ outcnt = 0; @@ -206,8 +372,16 @@ void *unzip(void *indata, unsigned long zbytes, void *target) makecrc(); gunzip(); + /* Verify that gunzip() consumed the entire input. */ + if (inbytes != 4) + error("compressed data length error"); + + /* Check the uncompressed data length and CRC. */ if ( bytes_out != dbytes ) - error("length error"); + error("uncompressed data length error"); + + if (orig_crc != CRC_VALUE) + error("crc error"); puts("ok\n"); -- 2.7.4