The current behavior of LZMA_FINISH in the decoder is a little
confusing because it requires calling lzma_code a few times without
providing more input to trigger a LZMA_BUF_ERROR. This patch replaces
return LZMA_OK lines with:

return action == LZMA_FINISH && *out_pos != out_size ? LZMA_BUF_ERROR : LZMA_OK;

This checks if the action is LZMA_FINISH and the output is not full,
then return the LZMA_BUF_ERROR. If the output is full, LZMA_OK is
needed because the caller needs to provide more room for the coder to
write out.
I believe this solves the issue in all cases and does not return
LZMA_BUF_ERROR when it shouldn't.

Jia Tan
From c7ebdb16a4f14fed641d82e97331eb39c4f57d1d Mon Sep 17 00:00:00 2001
From: jiat75 <jiat0...@gmail.com>
Date: Thu, 21 Apr 2022 21:09:57 +0800
Subject: [PATCH] LZMA_FINISH will now trigger LZMA_BUF_ERROR on truncated xz files right away

 In the single threaded and multithreaded decoder, LZMA_FINISH
 will cause BUF_ERROR right away instead of requiring multiple calls to
 lzma_code without providing more input.

---
 src/liblzma/common/common.c            |  1 -
 src/liblzma/common/stream_decoder.c    | 19 ++++++++++++++-----
 src/liblzma/common/stream_decoder_mt.c | 19 ++++++++++++++-----
 3 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/src/liblzma/common/common.c b/src/liblzma/common/common.c
index 346fc7af..3ddaf199 100644
--- a/src/liblzma/common/common.c
+++ b/src/liblzma/common/common.c
@@ -352,7 +352,6 @@ lzma_code(lzma_stream *strm, lzma_action action)
 
 	default:
 		// All the other errors are fatal; coding cannot be continued.
-		assert(ret != LZMA_BUF_ERROR);
 		strm->internal->sequence = ISEQ_ERROR;
 		break;
 	}
diff --git a/src/liblzma/common/stream_decoder.c b/src/liblzma/common/stream_decoder.c
index fdd8ff2f..06d4136b 100644
--- a/src/liblzma/common/stream_decoder.c
+++ b/src/liblzma/common/stream_decoder.c
@@ -118,7 +118,8 @@ stream_decode(void *coder_ptr, const lzma_allocator *allocator,
 
 		// Return if we didn't get the whole Stream Header yet.
 		if (coder->pos < LZMA_STREAM_HEADER_SIZE)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		coder->pos = 0;
 
@@ -161,7 +162,8 @@ stream_decode(void *coder_ptr, const lzma_allocator *allocator,
 
 	case SEQ_BLOCK_HEADER: {
 		if (*in_pos >= in_size)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		if (coder->pos == 0) {
 			// Detect if it's Index.
@@ -184,7 +186,8 @@ stream_decode(void *coder_ptr, const lzma_allocator *allocator,
 
 		// Return if we didn't get the whole Block Header yet.
 		if (coder->pos < coder->block_options.header_size)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		coder->pos = 0;
 
@@ -256,6 +259,10 @@ stream_decode(void *coder_ptr, const lzma_allocator *allocator,
 				in, in_pos, in_size, out, out_pos, out_size,
 				action);
 
+		if (ret == LZMA_OK)
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
+
 		if (ret != LZMA_STREAM_END)
 			return ret;
 
@@ -275,7 +282,8 @@ stream_decode(void *coder_ptr, const lzma_allocator *allocator,
 		// lzma_index_hash_decode() since it would return
 		// LZMA_BUF_ERROR, which we must not do here.
 		if (*in_pos >= in_size)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		// Decode the Index and compare it to the hash calculated
 		// from the sizes of the Blocks (if any).
@@ -296,7 +304,8 @@ stream_decode(void *coder_ptr, const lzma_allocator *allocator,
 
 		// Return if we didn't get the whole Stream Footer yet.
 		if (coder->pos < LZMA_STREAM_HEADER_SIZE)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		coder->pos = 0;
 
diff --git a/src/liblzma/common/stream_decoder_mt.c b/src/liblzma/common/stream_decoder_mt.c
index e8939254..1519e570 100644
--- a/src/liblzma/common/stream_decoder_mt.c
+++ b/src/liblzma/common/stream_decoder_mt.c
@@ -1053,7 +1053,8 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator,
 
 		// Return if we didn't get the whole Stream Header yet.
 		if (coder->pos < LZMA_STREAM_HEADER_SIZE)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		coder->pos = 0;
 
@@ -1153,7 +1154,8 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator,
 				break;
 			}
 
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 		}
 
 		if (ret == LZMA_INDEX_DETECTED) {
@@ -1513,7 +1515,8 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator,
 		// Return if the input didn't contain the whole Block.
 		if (coder->thr->in_filled < coder->thr->in_size) {
 			assert(*in_pos == in_size);
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 		}
 
 		// The whole Block has been copied to the thread-specific
@@ -1577,6 +1580,10 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator,
 		coder->progress_in += *in_pos - in_old;
 		coder->progress_out += *out_pos - out_old;
 
+		if (ret == LZMA_OK)
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
+
 		if (ret != LZMA_STREAM_END)
 			return ret;
 
@@ -1610,7 +1617,8 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator,
 		// lzma_index_hash_decode() since it would return
 		// LZMA_BUF_ERROR, which we must not do here.
 		if (*in_pos >= in_size)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		// Decode the Index and compare it to the hash calculated
 		// from the sizes of the Blocks (if any).
@@ -1635,7 +1643,8 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator,
 
 		// Return if we didn't get the whole Stream Footer yet.
 		if (coder->pos < LZMA_STREAM_HEADER_SIZE)
-			return LZMA_OK;
+			return action == LZMA_FINISH && *out_pos != out_size
+					? LZMA_BUF_ERROR : LZMA_OK;
 
 		coder->pos = 0;
 
-- 
2.25.1

Reply via email to