From c9ba4fb2839516a478a8ea6e5e264f46f12cfb5c Mon Sep 17 00:00:00 2001 From: OmniBlade Date: Fri, 7 Aug 2020 13:30:55 +0100 Subject: [PATCH] Prevents LCW_Uncompress overrunning a buffer on bad data. 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 #103. --- common/lcw.cpp | 63 ++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/common/lcw.cpp b/common/lcw.cpp index 0e73e93c5..80736ef60 100644 --- a/common/lcw.cpp +++ b/common/lcw.cpp @@ -32,6 +32,7 @@ * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "lcw.h" +#include /*************************************************************************** * LCW_Uncompress -- Decompress an LCW encoded data block. * @@ -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++; @@ -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++; @@ -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++; } @@ -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++; @@ -155,6 +158,10 @@ 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++; } @@ -162,6 +169,8 @@ int LCW_Uncompress(void const* source, void* dest, unsigned) } } } + + return (int)(dest_ptr - (unsigned char*)dest); } #ifdef NOASM