1
0
Fork 0
mirror of https://git.tukaani.org/xz.git synced 2024-04-04 12:36:23 +02:00

liblzma: Fix infinite loop in LZMA encoder init with dict_size >= 2 GiB.

The encoder doesn't support dictionary sizes larger than 1536 MiB.
This is validated, for example, when calculating the memory usage
via lzma_raw_encoder_memusage(). It is also enforced by the LZ
part of the encoder initialization. However, LZMA encoder with
LZMA_MODE_NORMAL did an unsafe calculation with dict_size before
such validation and that results in an infinite loop if dict_size
was 2 << 30 or greater.
This commit is contained in:
Lasse Collin 2022-11-22 11:20:17 +02:00
parent 1946b2b141
commit 5fecba6022

View file

@ -559,10 +559,9 @@ lzma_lzma_encoder_create(void **coder_ptr,
lzma_lzma1_encoder *coder = *coder_ptr; lzma_lzma1_encoder *coder = *coder_ptr;
// Set compression mode. We haven't validates the options yet, // Set compression mode. Note that we haven't validated the options
// but it's OK here, since nothing bad happens with invalid // yet. Invalid options will get rejected by lzma_lzma_encoder_reset()
// options in the code below, and they will get rejected by // call at the end of this function.
// lzma_lzma_encoder_reset() call at the end of this function.
switch (options->mode) { switch (options->mode) {
case LZMA_MODE_FAST: case LZMA_MODE_FAST:
coder->fast_mode = true; coder->fast_mode = true;
@ -573,6 +572,18 @@ lzma_lzma_encoder_create(void **coder_ptr,
// Set dist_table_size. // Set dist_table_size.
// Round the dictionary size up to next 2^n. // Round the dictionary size up to next 2^n.
//
// Currently the maximum encoder dictionary size
// is 1.5 GiB due to lz_encoder.c and here we need
// to be below 2 GiB to make the rounded up value
// fit in an uint32_t and avoid an infite while-loop
// (and undefined behavior due to a too large shift).
// So do the same check as in LZ encoder,
// limiting to 1.5 GiB.
if (options->dict_size > (UINT32_C(1) << 30)
+ (UINT32_C(1) << 29))
return LZMA_OPTIONS_ERROR;
uint32_t log_size = 0; uint32_t log_size = 0;
while ((UINT32_C(1) << log_size) < options->dict_size) while ((UINT32_C(1) << log_size) < options->dict_size)
++log_size; ++log_size;