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

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
...@@ -313,32 +313,38 @@ static inline uint8_t INIT peek_old_byte(struct writer *wr, ...@@ -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; wr->buffer[wr->buffer_pos++] = wr->previous_byte = byte;
if (wr->flush && wr->buffer_pos == wr->header->dict_size) { if (wr->flush && wr->buffer_pos == wr->header->dict_size) {
wr->buffer_pos = 0; wr->buffer_pos = 0;
wr->global_pos += wr->header->dict_size; 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) uint32_t rep0, int len)
{ {
do { do {
copy_byte(wr, rep0); if (copy_byte(wr, rep0))
return -1;
len--; len--;
} while (len != 0 && wr->buffer_pos < wr->header->dst_size); } 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, struct cstate *cst, uint16_t *p,
int pos_state, uint16_t *prob, int pos_state, uint16_t *prob,
int lc, uint32_t literal_pos_mask) { int lc, uint32_t literal_pos_mask) {
...@@ -372,16 +378,17 @@ static inline void INIT process_bit0(struct writer *wr, struct rc *rc, ...@@ -372,16 +378,17 @@ static inline void INIT process_bit0(struct writer *wr, struct rc *rc,
uint16_t *prob_lit = prob + mi; uint16_t *prob_lit = prob + mi;
rc_get_bit(rc, prob_lit, &mi); rc_get_bit(rc, prob_lit, &mi);
} }
write_byte(wr, mi);
if (cst->state < 4) if (cst->state < 4)
cst->state = 0; cst->state = 0;
else if (cst->state < 10) else if (cst->state < 10)
cst->state -= 3; cst->state -= 3;
else else
cst->state -= 6; 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, struct cstate *cst, uint16_t *p,
int pos_state, uint16_t *prob) { int pos_state, uint16_t *prob) {
int offset; int offset;
...@@ -412,8 +419,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, ...@@ -412,8 +419,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc,
cst->state = cst->state < LZMA_NUM_LIT_STATES ? cst->state = cst->state < LZMA_NUM_LIT_STATES ?
9 : 11; 9 : 11;
copy_byte(wr, cst->rep0); return copy_byte(wr, cst->rep0);
return;
} else { } else {
rc_update_bit_1(rc, prob); rc_update_bit_1(rc, prob);
} }
...@@ -515,12 +521,12 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, ...@@ -515,12 +521,12 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc,
} else } else
cst->rep0 = pos_slot; cst->rep0 = pos_slot;
if (++(cst->rep0) == 0) if (++(cst->rep0) == 0)
return; return 0;
} }
len += LZMA_MATCH_MIN_LEN; 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, ...@@ -623,11 +629,17 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len,
int pos_state = get_pos(&wr) & pos_state_mask; int pos_state = get_pos(&wr) & pos_state_mask;
uint16_t *prob = p + LZMA_IS_MATCH + uint16_t *prob = p + LZMA_IS_MATCH +
(cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state; (cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state;
if (rc_is_bit_0(&rc, prob)) if (rc_is_bit_0(&rc, prob)) {
process_bit0(&wr, &rc, &cst, p, pos_state, prob, if (process_bit0(&wr, &rc, &cst, p, pos_state, prob,
lc, literal_pos_mask); lc, literal_pos_mask)) {
else { error("LZMA data is corrupt");
process_bit1(&wr, &rc, &cst, p, pos_state, prob); 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) if (cst.rep0 == 0)
break; break;
} }
...@@ -637,9 +649,8 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len, ...@@ -637,9 +649,8 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len,
if (posp) if (posp)
*posp = rc.ptr-rc.buffer; *posp = rc.ptr-rc.buffer;
if (wr.flush) if (!wr.flush || wr.flush(wr.buffer, wr.buffer_pos) == wr.buffer_pos)
wr.flush(wr.buffer, wr.buffer_pos); ret = 0;
ret = 0;
exit_3: exit_3:
large_free(p); large_free(p);
exit_2: exit_2:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment