diff src/lzfu.c @ 41:183ae993b9ad

security fix for potential buffer overrun in lz decompress
author carl
date Tue, 02 Oct 2007 15:49:44 -0700
parents ddfb25318812
children f6db1f060a95
line wrap: on
line diff
--- a/src/lzfu.c	Fri Aug 24 18:57:59 2007 -0700
+++ b/src/lzfu.c	Tue Oct 02 15:49:44 2007 -0700
@@ -15,14 +15,6 @@
 #include <string.h>
 #include <stdio.h>
 
-#ifndef _MSC_VER
-#include <stdint.h>
-#endif
-
-#ifdef _MSC_VER
-#define uint32_t unsigned int
-#endif
-
 #include "lzfu.h"
 
 #define LZFU_COMPRESSED 		0x75465a4c
@@ -30,28 +22,24 @@
 
 // initital dictionary
 #define LZFU_INITDICT	"{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \
-												 "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscrip" \
-												 "t \\fdecor MS Sans SerifSymbolArialTimes Ne" \
-												 "w RomanCourier{\\colortbl\\red0\\green0\\blue0" \
-												 "\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab" \
-												 "\\tx"
+						"{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscrip" \
+						"t \\fdecor MS Sans SerifSymbolArialTimes Ne" \
+						"w RomanCourier{\\colortbl\\red0\\green0\\blue0" \
+						"\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab" \
+						"\\tx"
 // initial length of dictionary
 #define LZFU_INITLENGTH 207
 
 // header for compressed rtf
 typedef struct _lzfuheader {
-	uint32_t cbSize;
-	uint32_t cbRawSize;
-	uint32_t dwMagic;
-	uint32_t dwCRC;
+	u_int32_t cbSize;
+	u_int32_t cbRawSize;
+	u_int32_t dwMagic;
+	u_int32_t dwCRC;
 } lzfuheader;
 
 
-/**
-	We always need to add 0x10 to the buffer offset because we need to skip past the header info
-*/
-
-unsigned char* lzfu_decompress (unsigned char* rtfcomp, size_t *size) {
+unsigned char* lzfu_decompress (unsigned char* rtfcomp, u_int32_t compsize, size_t *size) {
 	// the dictionary buffer
 	unsigned char dict[4096];
 	// the dictionary pointer
@@ -62,9 +50,12 @@
 	unsigned char flags;
 	// temp value for determining the bits in the flag
 	unsigned char flag_mask;
-	unsigned int i, in_size;
+	u_int32_t i;
 	unsigned char *out_buf;
-	unsigned int out_ptr = 0;
+	u_int32_t out_ptr  = 0;
+	u_int32_t out_size;
+	u_int32_t in_ptr;
+	u_int32_t in_size;
 
 	memcpy(dict, LZFU_INITDICT, LZFU_INITLENGTH);
 	dict_length = LZFU_INITLENGTH;
@@ -73,58 +64,61 @@
 	LE32_CPU(lzfuhdr.cbRawSize);
 	LE32_CPU(lzfuhdr.dwMagic);
 	LE32_CPU(lzfuhdr.dwCRC);
-	/*	printf("total size: %d\n", lzfuhdr.cbSize+4);
-	printf("raw size  : %d\n", lzfuhdr.cbRawSize);
-	printf("compressed: %s\n", (lzfuhdr.dwMagic == LZFU_COMPRESSED ? "yes" : "no"));
-	printf("CRC       : %#x\n", lzfuhdr.dwCRC);
-	printf("\n");*/
-	out_buf = (unsigned char*)xmalloc(lzfuhdr.cbRawSize+20); //plus 4 cause we have 2x'}' and a \0
-	in_size = 0;
-	// we add plus one here cause when referencing an array, the index is always one less
-	// (ie, when accessing 2 element array, highest index is [1])
-	while (in_size+0x11 < lzfuhdr.cbSize) {
-		memcpy(&flags, &(rtfcomp[in_size+0x10]), 1);
-		in_size += 1;
-
+	//printf("total size: %d\n", lzfuhdr.cbSize+4);
+	//printf("raw size  : %d\n", lzfuhdr.cbRawSize);
+	//printf("compressed: %s\n", (lzfuhdr.dwMagic == LZFU_COMPRESSED ? "yes" : "no"));
+	//printf("CRC       : %#x\n", lzfuhdr.dwCRC);
+	//printf("\n");
+	out_size = lzfuhdr.cbRawSize + 3;	// two braces and a null terminator
+	out_buf  = (unsigned char*)xmalloc(out_size);
+	in_ptr	 = sizeof(lzfuhdr);
+	in_size  = (lzfuhdr.cbSize < compsize) ? lzfuhdr.cbSize : compsize;
+	while (in_ptr < in_size) {
+		flags = rtfcomp[in_ptr++];
 		flag_mask = 1;
-		while (flag_mask != 0 && in_size+0x11 < lzfuhdr.cbSize) {
+		while (flag_mask) {
 			if (flag_mask & flags) {
-				// read 2 bytes from input
-				unsigned short int blkhdr, offset, length;
-				memcpy(&blkhdr, &(rtfcomp[in_size+0x10]), 2);
-				LE16_CPU(blkhdr);
-				in_size += 2;
-				/* swap the upper and lower bytes of blkhdr */
-				blkhdr = (((blkhdr&0xFF00)>>8)+
-					  ((blkhdr&0x00FF)<<8));
-				/* the offset is the first 24 bits of the 32 bit value */
-				offset = (blkhdr&0xFFF0)>>4;
-				/* the length of the dict entry are the last 8 bits */
-				length = (blkhdr&0x000F)+2;
-				// add the value we are about to print to the dictionary
-				for (i=0; i < length; i++) {
-					unsigned char c1;
-					c1 = dict[(offset+i)%4096];
-					dict[dict_length]=c1;
-					dict_length = (dict_length+1) % 4096;
-					out_buf[out_ptr++] = c1;
+				// two bytes available?
+				if (in_ptr+1 < in_size) {
+					// read 2 bytes from input
+					unsigned short int blkhdr, offset, length;
+					memcpy(&blkhdr, rtfcomp+in_ptr, 2);
+					LE16_CPU(blkhdr);
+					in_ptr += 2;
+					/* swap the upper and lower bytes of blkhdr */
+					blkhdr = (((blkhdr&0xFF00)>>8)+
+							  ((blkhdr&0x00FF)<<8));
+					/* the offset is the first 12 bits of the 16 bit value */
+					offset = (blkhdr&0xFFF0)>>4;
+					/* the length of the dict entry are the last 4 bits */
+					length = (blkhdr&0x000F)+2;
+					// add the value we are about to print to the dictionary
+					for (i=0; i < length; i++) {
+						unsigned char c1;
+						c1 = dict[(offset+i)%4096];
+						dict[dict_length]=c1;
+						dict_length = (dict_length+1) % 4096;
+						if (out_ptr < out_size) out_buf[out_ptr++] = c1;
+					}
 				}
 			} else {
-				// uncompressed chunk (single byte)
-				char c1 = rtfcomp[in_size+0x10];
-				in_size ++;
-				dict[dict_length] = c1;
-				dict_length = (dict_length+1)%4096;
-				out_buf[out_ptr++] = c1;
+				// one byte available?
+				if (in_ptr < in_size) {
+					// uncompressed chunk (single byte)
+					char c1 = rtfcomp[in_ptr++];
+					dict[dict_length] = c1;
+					dict_length = (dict_length+1)%4096;
+					if (out_ptr < out_size) out_buf[out_ptr++] = c1;
+				}
 			}
 			flag_mask <<= 1;
 		}
 	}
-	// the compressed version doesn't appear to drop the closing braces onto the doc.
-	// we should do that
-	out_buf[out_ptr++] = '}';
-	out_buf[out_ptr++] = '}';
+	// the compressed version doesn't appear to drop the closing
+	// braces onto the doc, so we do that here.
+	if (out_ptr < out_size) out_buf[out_ptr++] = '}';
+	if (out_ptr < out_size) out_buf[out_ptr++] = '}';
 	*size = out_ptr;
-	out_buf[out_ptr++] = '\0';
+	if (out_ptr < out_size) out_buf[out_ptr++] = '\0';
 	return out_buf;
 }