Commit 9b4841c6 for xz
commit 9b4841c671e63d49d210f9753cb118515a381f0a
Author: Lasse Collin <lasse.collin@tukaani.org>
Date: Wed Nov 4 18:37:22 2026 +0200
liblzma: Fix optional LZMA end marker detection
When uncompressed size was known and EOPM was allowed, the EOPM could
still be rejected with LZMA_DATA_ERROR. It happened when the decoding
of the EOPM symbol had been started (eopm_is_valid was set to true),
but there wasn't enough input to fully decode the EOPM symbol yet.
On the next lzma_code() call with more input, the local variable
eopm_is_valid was initialized to false and the EOPM symbol was rejected.
Fix by storing eopm_is_valid in *coder. Once it has been set to true, it
can never become false again. (NOTE: One needs to be careful. For example,
might_finish_without_eopm cannot be put to *coder because a later
lzma_code() call might provide less output space than an earlier call,
and so might_finish_without_eopm can flip between true and false.)
This bug affected (1) decoding of unusual .lzma files and
(2) raw decoding with LZMA_FILTER_LZMA1EXT when the flag
LZMA_LZMA1EXT_ALLOW_EOPM is set. Support for (1) was added in
5.2.6 (2022-08-12) but due to this bug it didn't always work.
Support for (2) was added in 5.3.5beta (2022-12-01).
Fixes: 9595a3119b9f ("liblzma: Add optional autodetection of LZMA end marker.")
Fixes: https://github.com/tukaani-project/xz/issues/260
Reported-by: GitHub user tiendc
diff --git a/src/liblzma/lzma/lzma_decoder.c b/src/liblzma/lzma/lzma_decoder.c
index 2088a2fa..47383e06 100644
--- a/src/liblzma/lzma/lzma_decoder.c
+++ b/src/liblzma/lzma/lzma_decoder.c
@@ -179,6 +179,15 @@ typedef struct {
/// This is ignored if uncompressed_size == LZMA_VLI_UNKNOWN.
bool allow_eopm;
+ /// This is true when the next LZMA symbol is allowed to be EOPM.
+ /// That is, if this is false, then EOPM is considered
+ /// an invalid symbol and we will return LZMA_DATA_ERROR.
+ ///
+ /// This is always true when uncompressed_size == LZMA_VLI_UNKNOWN.
+ /// If allow_eopm is true, eopm_is_valid might be set to true after
+ /// the expected amount of uncompressed output has been produced.
+ bool eopm_is_valid;
+
////////////////////////////////
// State of incomplete symbol //
////////////////////////////////
@@ -288,15 +297,6 @@ lzma_decode(void *coder_ptr, lzma_dict *restrict dictptr,
lzma_ret ret = LZMA_OK;
- // This is true when the next LZMA symbol is allowed to be EOPM.
- // That is, if this is false, then EOPM is considered
- // an invalid symbol and we will return LZMA_DATA_ERROR.
- //
- // EOPM is always required (not just allowed) when
- // the uncompressed size isn't known. When uncompressed size
- // is known, eopm_is_valid may be set to true later.
- bool eopm_is_valid = coder->uncompressed_size == LZMA_VLI_UNKNOWN;
-
// If uncompressed size is known and there is enough output space
// to decode all the data, limit the available buffer space so that
// the main loop won't try to decode past the end of the stream.
@@ -684,7 +684,7 @@ slow:
// Otherwise continue decoding with the expectation
// that the next LZMA symbol is EOPM.
- eopm_is_valid = true;
+ coder->eopm_is_valid = true;
}
rc_if_0_safe(coder->is_match[state][pos_state], SEQ_IS_MATCH) {
@@ -856,7 +856,7 @@ slow:
#ifndef HAVE_SMALL
eopm:
#endif
- if (!eopm_is_valid) {
+ if (!coder->eopm_is_valid) {
ret = LZMA_DATA_ERROR;
goto out;
}
@@ -1028,6 +1028,7 @@ lzma_decoder_uncompressed(void *coder_ptr, lzma_vli uncompressed_size,
lzma_lzma1_decoder *coder = coder_ptr;
coder->uncompressed_size = uncompressed_size;
coder->allow_eopm = allow_eopm;
+ coder->eopm_is_valid = uncompressed_size == LZMA_VLI_UNKNOWN;
}