summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhpa <hpa>2003-07-15 07:36:55 +0000
committerhpa <hpa>2003-07-15 07:36:55 +0000
commitdac52b6c5894ed88e832f8408754a13c06f94ed1 (patch)
treeaf855330fd09df72704e6b327344f89a8bbc1e9d
parent80e84b3714db5a355729a535cfad6ad2cfd1ef30 (diff)
downloadsyslinux-dac52b6c5894ed88e832f8408754a13c06f94ed1.tar.gz
Fix overlap handling properly (we hope)
-rw-r--r--memdisk/memdisk.h2
-rw-r--r--memdisk/setup.c69
-rw-r--r--memdisk/unzip.c21
3 files changed, 57 insertions, 35 deletions
diff --git a/memdisk/memdisk.h b/memdisk/memdisk.h
index ae07301c..bc125ccd 100644
--- a/memdisk/memdisk.h
+++ b/memdisk/memdisk.h
@@ -38,6 +38,6 @@ extern void __attribute__((noreturn)) die(void);
#define memset(a,b,c) __builtin_memset(a,b,c)
/* Decompression */
-void *unzip(void *indata, uint32_t *datalen, void *end_mem);
+void *unzip(void *indata, unsigned long zbytes, void *target);
#endif
diff --git a/memdisk/setup.c b/memdisk/setup.c
index e33f2dd0..8303aceb 100644
--- a/memdisk/setup.c
+++ b/memdisk/setup.c
@@ -222,45 +222,84 @@ const char *getcmditem(const char *what)
/*
* Check to see if this is a gzip image
*/
+#define UNZIP_ALIGN 512
+
+extern void _end; /* Symbol signalling end of data */
+
void unzip_if_needed(uint32_t *where_p, uint32_t *size_p)
{
uint32_t where = *where_p;
uint32_t size = *size_p;
uint64_t startrange, endrange;
- uint32_t gzdatasize;
- uint32_t end_mem = 0;
+ uint32_t gzdatasize, gzwhere;
+ uint32_t target = 0;
int i, okmem;
/* Is it a gzip image? */
if ( *(uint16_t *)where == 0x8b1f ) {
gzdatasize = *(uint32_t *)(where + size - 4);
- /* Find a good place to put it */
+ /* Find a good place to put it: search memory ranges in descending order
+ until we find one that is legal and fits */
okmem = 0;
- for ( i = 0 ; i < nranges ; i++ ) {
+ for ( i = nranges-1 ; i >= 0 ; i-- ) {
+ /* Don't use > 4G memory */
+ if ( ranges[i].start >= 0x100000000ULL )
+ continue;
+ startrange = ranges[i].start;
+
/* Truncate range at 4G if needed */
- endrange = (ranges[i+1].start >= 0x100000000ULL
+ endrange = ((ranges[i+1].start >= 0x100000000ULL ||
+ ranges[i+1].start == 0ULL)
? 100000000ULL : ranges[i+1].start);
- /* Allow for 512-byte alignment */
- startrange = (ranges[i].start + 511) & ~511;
- if ( startrange <= (-gzdatasize) &&
- ranges[i].type == 1 &&
- endrange - startrange >= gzdatasize ) {
- end_mem = (uint32_t)endrange;
+ /* Make sure we don't overwrite ourselves */
+ if ( startrange < (uint32_t)&_end )
+ startrange = (uint64_t)&_end;
+
+ /* Allow for alignment */
+ startrange = (ranges[i].start + (UNZIP_ALIGN-1)) & ~(UNZIP_ALIGN-1);
+
+ /* This is where the gz image should be put if we put it in this range */
+ gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN-1);
+
+ /* Must be memory and large enough */
+ if ( ranges[i].type == 1 && gzwhere >= startrange ) {
+ if ( where+size >= gzwhere && where < endrange ) {
+ /* Need to move source data to avoid compressed/uncompressed overlap */
+ uint32_t newwhere;
+
+ if ( gzwhere-startrange < size )
+ continue; /* Can't fit both old and new */
+
+ newwhere = (gzwhere - size) & ~(UNZIP_ALIGN-1);
+ printf("Moving compressed data from 0x%08x to 0x%08x\n",
+ where, newwhere);
+
+ /* Our memcpy() is OK, because we always move from a higher
+ address to a lower one */
+ memcpy((void *)newwhere, (void *)where, size);
+ where = newwhere;
+ }
+
+ target = gzwhere;
okmem = 1;
+ break;
}
- }
-
+ }
if ( !okmem ) {
puts("Not enough memory to decompress image\n");
die();
}
- *where_p = (uint32_t)unzip((void *)where, size_p, (void *)end_mem);
+ printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
+ target, gzdatasize);
+
+ *size_p = gzdatasize;
+ *where_p = (uint32_t)unzip((void *)where, size, (void *)target);
}
-}
+}
/*
* Figure out the "geometry" of the disk in question
diff --git a/memdisk/unzip.c b/memdisk/unzip.c
index 02f9f4ae..94f56e43 100644
--- a/memdisk/unzip.c
+++ b/memdisk/unzip.c
@@ -166,10 +166,8 @@ static void error(char *x)
*/
extern void _end;
-void *unzip(void *indata, uint32_t *datalen, void *end_mem)
+void *unzip(void *indata, unsigned long zbytes, void *target)
{
- void *target;
- unsigned long zbytes = *datalen;
/* The uncompressed length of a gzip file is the last four bytes */
unsigned long dbytes = *(uint32_t *)((char *)indata + zbytes - 4);
@@ -177,23 +175,8 @@ void *unzip(void *indata, uint32_t *datalen, void *end_mem)
free_mem_ptr = (ulg)sys_bounce + 0x10000;
free_mem_end_ptr = free_mem_ptr + 0x10000;
- /* Pick address and round to nearest sector */
- target = (void *)(((uint32_t)end_mem - dbytes) & ~511);
- printf("gzip image: decompressed addr 0x%08lx, len 0x%08lx: ", (unsigned long)target, dbytes);
- *datalen = dbytes;
-
- /* Copy input data to "low high" memory */
- inbuf = &_end;
- inbuf = (void *)(((unsigned long)inbuf + 3) & ~3);
-
- if ( (uint32_t)inbuf + zbytes > (uint32_t)target ||
- (void *)inbuf > indata )
- error("insufficient memory to decompress");
-
- memcpy(inbuf, indata, zbytes);
-
/* Set up input buffer */
- inbuf = &_end;
+ inbuf = indata;
insize = zbytes;
/* Set up output buffer */