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: Lasse 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: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
278208d9d6
commit
528941ca05
@ -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,
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
} 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,
|
|||||||
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,
|
|||||||
|
|
||||||
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:
|
||||||
|
Loading…
Reference in New Issue
Block a user