Skip to content

Commit

Permalink
Prevents LCW_Uncompress overrunning a buffer on bad data.
Browse files Browse the repository at this point in the history
Clickmaps for TD have LCW data that causes writes larger than the
expected image size.
Unclear how they generated images with this bad data so no test case
provided.
Fixes electronicarts#103.
  • Loading branch information
OmniBlade committed Aug 7, 2020
1 parent 555bafa commit c9ba4fb
Showing 1 changed file with 36 additions and 27 deletions.
63 changes: 36 additions & 27 deletions common/lcw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "lcw.h"
#include <string.h>

/***************************************************************************
* LCW_Uncompress -- Decompress an LCW encoded data block. *
Expand Down Expand Up @@ -66,16 +67,17 @@
* HISTORY: *
* 03/20/1995 IML : Created. *
*=========================================================================*/
int LCW_Uncompress(void const* source, void* dest, unsigned)
int LCW_Uncompress(void const* source, void* dest, unsigned length)
{
unsigned char *source_ptr, *dest_ptr, *copy_ptr, op_code, data;
unsigned char *source_ptr, *dest_ptr, *copy_ptr, *dest_end, op_code, data;
unsigned count, *word_dest_ptr, word_data;

/* Copy the source and destination ptrs. */
source_ptr = (unsigned char*)source;
dest_ptr = (unsigned char*)dest;
dest_end = dest_ptr + length;

while (1 /*TRUE*/) {
while (dest_ptr < dest_end) {

/* Read in the operation code. */
op_code = *source_ptr++;
Expand All @@ -86,6 +88,11 @@ int LCW_Uncompress(void const* source, void* dest, unsigned)
count = (op_code >> 4) + 3;
copy_ptr = dest_ptr - ((unsigned)*source_ptr++ + (((unsigned)op_code & 0x0f) << 8));

/* Check we aren't going to write past the end of the destination buffer */
if (count > dest_end - dest_ptr) {
count = dest_end - dest_ptr;
}

while (count--)
*dest_ptr++ = *copy_ptr++;

Expand All @@ -96,13 +103,18 @@ int LCW_Uncompress(void const* source, void* dest, unsigned)
if (op_code == 0x80) {

/* Return # of destination bytes written. */
return ((unsigned long)(dest_ptr - (unsigned char*)dest));
return (int)(dest_ptr - (unsigned char*)dest);

} else {

/* Do a medium copy from source. */
count = op_code & 0x3f;

/* Check we aren't going to write past the end of the destination buffer */
if (count > dest_end - dest_ptr) {
count = dest_end - dest_ptr;
}

while (count--)
*dest_ptr++ = *source_ptr++;
}
Expand All @@ -112,38 +124,29 @@ int LCW_Uncompress(void const* source, void* dest, unsigned)
if (op_code == 0xfe) {

/* Do a long run. */
count = *source_ptr + ((unsigned)*(source_ptr + 1) << 8);
word_data = data = *(source_ptr + 2);
word_data = (word_data << 24) + (word_data << 16) + (word_data << 8) + word_data;
source_ptr += 3;

copy_ptr = dest_ptr + 4 - ((unsigned)dest_ptr & 0x3);
count -= (copy_ptr - dest_ptr);
while (dest_ptr < copy_ptr)
*dest_ptr++ = data;
count = *source_ptr++;
count += (*source_ptr++) << 8;

word_dest_ptr = (unsigned*)dest_ptr;

dest_ptr += (count & 0xfffffffc);

while (word_dest_ptr < (unsigned*)dest_ptr) {
*word_dest_ptr = word_data;
*(word_dest_ptr + 1) = word_data;
word_dest_ptr += 2;
if (count > dest_end - dest_ptr) {
count = dest_end - dest_ptr;
}

copy_ptr = dest_ptr + (count & 0x3);
while (dest_ptr < copy_ptr)
*dest_ptr++ = data;
memset(dest_ptr, (*source_ptr++), count);
dest_ptr += count;

} else {

if (op_code == 0xff) {

/* Do a long copy from destination. */
count = *source_ptr + ((unsigned)*(source_ptr + 1) << 8);
copy_ptr = (unsigned char*)dest + *(source_ptr + 2) + ((unsigned)*(source_ptr + 3) << 8);
source_ptr += 4;
count = *source_ptr++;
count += (*source_ptr++) << 8;
copy_ptr = (unsigned char*)dest + *source_ptr++;
copy_ptr += (*source_ptr++) << 8;

if (count > dest_end - dest_ptr) {
count = dest_end - dest_ptr;
}

while (count--)
*dest_ptr++ = *copy_ptr++;
Expand All @@ -155,13 +158,19 @@ int LCW_Uncompress(void const* source, void* dest, unsigned)
copy_ptr = (unsigned char*)dest + *source_ptr + ((unsigned)*(source_ptr + 1) << 8);
source_ptr += 2;

if (count > dest_end - dest_ptr) {
count = dest_end - dest_ptr;
}

while (count--)
*dest_ptr++ = *copy_ptr++;
}
}
}
}
}

return (int)(dest_ptr - (unsigned char*)dest);
}

#ifdef NOASM
Expand Down

0 comments on commit c9ba4fb

Please sign in to comment.