Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 528941ca authored by Lasse Collin's avatar Lasse Collin Committed by Linus Torvalds
Browse files

Decompressors: check for write errors in decompress_unlzma.c



The return value of wr->flush() is not checked in write_byte().  This
means that the decompressor won't stop even if the caller doesn't want
more data.  This can happen e.g.  with corrupt LZMA-compressed initramfs.
Returning the error quickly allows the user to see the error message
quicker.

There is a similar missing check for wr.flush() near the end of unlzma().

Signed-off-by: default avatarLasse Collin <lasse.collin@tukaani.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Alain Knaff <alain@knaff.lu>
Cc: Albin Tonnerre <albin.tonnerre@free-electrons.com>
Cc: Phillip Lougher <phillip@lougher.demon.co.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 278208d9
Loading
Loading
Loading
Loading
+32 −21
Original line number Diff line number Diff line
@@ -313,32 +313,38 @@ static inline uint8_t INIT peek_old_byte(struct writer *wr,

}

static inline void INIT write_byte(struct writer *wr, uint8_t byte)
static inline int INIT write_byte(struct writer *wr, uint8_t byte)
{
	wr->buffer[wr->buffer_pos++] = wr->previous_byte = byte;
	if (wr->flush && wr->buffer_pos == wr->header->dict_size) {
		wr->buffer_pos = 0;
		wr->global_pos += wr->header->dict_size;
		wr->flush((char *)wr->buffer, wr->header->dict_size);
		if (wr->flush((char *)wr->buffer, wr->header->dict_size)
				!= wr->header->dict_size)
			return -1;
	}
	return 0;
}


static inline void INIT copy_byte(struct writer *wr, uint32_t offs)
static inline int INIT copy_byte(struct writer *wr, uint32_t offs)
{
	write_byte(wr, peek_old_byte(wr, offs));
	return write_byte(wr, peek_old_byte(wr, offs));
}

static inline void INIT copy_bytes(struct writer *wr,
static inline int INIT copy_bytes(struct writer *wr,
					 uint32_t rep0, int len)
{
	do {
		copy_byte(wr, rep0);
		if (copy_byte(wr, rep0))
			return -1;
		len--;
	} while (len != 0 && wr->buffer_pos < wr->header->dst_size);

	return len;
}

static inline void INIT process_bit0(struct writer *wr, struct rc *rc,
static inline int INIT process_bit0(struct writer *wr, struct rc *rc,
				     struct cstate *cst, uint16_t *p,
				     int pos_state, uint16_t *prob,
				     int lc, uint32_t literal_pos_mask) {
@@ -372,16 +378,17 @@ static inline void INIT process_bit0(struct writer *wr, struct rc *rc,
		uint16_t *prob_lit = prob + mi;
		rc_get_bit(rc, prob_lit, &mi);
	}
	write_byte(wr, mi);
	if (cst->state < 4)
		cst->state = 0;
	else if (cst->state < 10)
		cst->state -= 3;
	else
		cst->state -= 6;

	return write_byte(wr, mi);
}

static inline void INIT process_bit1(struct writer *wr, struct rc *rc,
static inline int INIT process_bit1(struct writer *wr, struct rc *rc,
					    struct cstate *cst, uint16_t *p,
					    int pos_state, uint16_t *prob) {
  int offset;
@@ -412,8 +419,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc,

				cst->state = cst->state < LZMA_NUM_LIT_STATES ?
					9 : 11;
				copy_byte(wr, cst->rep0);
				return;
				return copy_byte(wr, cst->rep0);
			} else {
				rc_update_bit_1(rc, prob);
			}
@@ -515,12 +521,12 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc,
		} else
			cst->rep0 = pos_slot;
		if (++(cst->rep0) == 0)
			return;
			return 0;
	}

	len += LZMA_MATCH_MIN_LEN;

	copy_bytes(wr, cst->rep0, len);
	return copy_bytes(wr, cst->rep0, len);
}


@@ -623,11 +629,17 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len,
		int pos_state =	get_pos(&wr) & pos_state_mask;
		uint16_t *prob = p + LZMA_IS_MATCH +
			(cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state;
		if (rc_is_bit_0(&rc, prob))
			process_bit0(&wr, &rc, &cst, p, pos_state, prob,
				     lc, literal_pos_mask);
		else {
			process_bit1(&wr, &rc, &cst, p, pos_state, prob);
		if (rc_is_bit_0(&rc, prob)) {
			if (process_bit0(&wr, &rc, &cst, p, pos_state, prob,
					lc, literal_pos_mask)) {
				error("LZMA data is corrupt");
				goto exit_3;
			}
		} else {
			if (process_bit1(&wr, &rc, &cst, p, pos_state, prob)) {
				error("LZMA data is corrupt");
				goto exit_3;
			}
			if (cst.rep0 == 0)
				break;
		}
@@ -637,8 +649,7 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len,

	if (posp)
		*posp = rc.ptr-rc.buffer;
	if (wr.flush)
		wr.flush(wr.buffer, wr.buffer_pos);
	if (!wr.flush || wr.flush(wr.buffer, wr.buffer_pos) == wr.buffer_pos)
		ret = 0;
exit_3:
	large_free(p);