Support lzma images with unset file size

The lzma compression tool stores the size of the uncompressed file in
the header of the lzma output file. However, if it reads its input data
over stdin, it just stores 0xFFFFFFFF as the file size meaning the file
size is unknown.

The Linux kernel makefiles use lzma in the latter mode where we end up
with an lzma file that does not have the file size stored in the header.

This change is similar to the upstream commit
f68ab43de67f59925542efb6bcec30f4a84fe695

Change-Id: I17993c22afb2b264673f2f5948bd029ff8ad54ff
diff --git a/lib_generic/lzma/LzmaTools.c b/lib_generic/lzma/LzmaTools.c
index f01e827..5e69eb8 100644
--- a/lib_generic/lzma/LzmaTools.c
+++ b/lib_generic/lzma/LzmaTools.c
@@ -57,6 +57,7 @@
 	int i;
 
 	SizeT outSizeFull = 0xFFFFFFFF; /* 4GBytes limit */
+	SizeT outAvail;
 	SizeT inProcessed;
 	SizeT outProcessed;
 	SizeT outSize;
@@ -101,15 +102,25 @@
 		 */
 	        outSizeFull |= (((SizeT)outSizeHigh << 16) << 16);
 	} else if (outSizeHigh != 0 || (UInt32)(SizeT)outSize != outSize) {
-	        /*
+		/*
 		 * SizeT is a 32 bit uint => We cannot manage files larger than
-		 * 4GB!
+		 * 4GB!  Assume however that all 0xf values is "unknown size" and
+		 * not actually a file of 2^64 bits.
 		 *
 		 */
-		debug ("LZMA: 64bit support not enabled.\n");
-	        return LZMA_RESULT_DATA_ERROR;
+		if (outSizeHigh != (SizeT)-1 || outSize != (SizeT)-1) {
+			debug ("LZMA: 64bit support not enabled.\n");
+			return LZMA_RESULT_DATA_ERROR;
+		}
 	}
 
+	/* Short-circuit early if we know the buffer can't hold the results. */
+	if (outSizeFull != (SizeT)-1 && *uncompressedSize < outSizeFull)
+		return LZMA_RESULT_DATA_ERROR;
+
+	outAvail = min(outSizeFull, *uncompressedSize);
+
+	debug ("LZMA: Available output buffer..... 0x%lx\n", (unsigned long) outAvail);
 	debug ("LZMA: Uncompresed size............ 0x%lx\n", (unsigned long) outSizeFull);
 	debug ("LZMA: Compresed size.............. 0x%lx\n", (unsigned long) compressedSize);
 
@@ -117,7 +128,7 @@
 	debug ("LZMA: Decompress buffer needed.... 0x%lx\n",
 			(unsigned long) LzmaGetNumProbs(&state.Properties) * sizeof(CProb));
 	/* decompression buffer lives at the end of the decompressed image area */
-	state.Probs = (void*)(((unsigned long) outStream + outSizeFull + 0x80) & ~(0x80 - 1));
+	state.Probs = (void*)(((unsigned long) outStream + outAvail + 0x80) & ~(0x80 - 1));
 #else
 	debug ("LZMA: Dynamic memory needed....... 0x%lx, allocating...\n",
 			(unsigned long) LzmaGetNumProbs(&state.Properties) * sizeof(CProb));
@@ -140,11 +151,25 @@
 
 	res = LzmaDecode(&state,
 		inStream + LZMA_DATA_OFFSET, compressedSize, &inProcessed,
-		outStream, outSizeFull,  &outProcessed);
+		outStream, outAvail,  &outProcessed);
 	if (res != LZMA_RESULT_OK)  {
 		return res;
 	}
 
+	debug ("LZMA: Uncompressed bytes out...... 0x%lx\n", (unsigned long) outProcessed);
+	debug ("LZMA: Compressed bytes in......... 0x%lx\n", (unsigned long) inProcessed);
+
+	if (outProcessed != outSizeFull && outProcessed == outAvail) {
+		/*
+		 * If the size of the uncompressed image matches exactly the
+		 * size of the output buffer, then we do not know whether that
+		 * is a just coincident or whether LzmaDecode stopped
+		 * prematurely due to insufficient output buffer space. Err on
+		 * the side of caution and return an error.
+		 * */
+		return LZMA_RESULT_DATA_ERROR;
+	}
+
 	*uncompressedSize = outProcessed;
 #ifndef LZMA_DECOMPRESS_NOALLOC
 	free(state.Probs);