Fix decompression size check

Dropbear's decompression could erroneously exit with
"bad packet, oversized decompressed"
for a valid 32768 byte decompressed payload (an off-by-one error).
It could be triggered particularly with larger SSH window sizes.

This change also simplifies the function by allocating a
single 32kB buffer rather than incrementally increasing the size.
This commit is contained in:
Matt Johnston 2022-10-25 21:17:56 +08:00
parent 86efbae708
commit f7d306e963

View File

@ -430,44 +430,32 @@ static buffer* buf_decompress(const buffer* buf, unsigned int len) {
z_streamp zstream; z_streamp zstream;
zstream = ses.keys->recv.zstream; zstream = ses.keys->recv.zstream;
ret = buf_new(len); /* We use RECV_MAX_PAYLOAD_LEN+1 here to ensure that
we can detect an oversized payload after inflate() */
ret = buf_new(RECV_MAX_PAYLOAD_LEN+1);
zstream->avail_in = len; zstream->avail_in = len;
zstream->next_in = buf_getptr(buf, len); zstream->next_in = buf_getptr(buf, len);
zstream->avail_out = ret->size;
/* decompress the payload, incrementally resizing the output buffer */ zstream->next_out = ret->data;
while (1) {
zstream->avail_out = ret->size - ret->pos;
zstream->next_out = buf_getwriteptr(ret, zstream->avail_out);
result = inflate(zstream, Z_SYNC_FLUSH); result = inflate(zstream, Z_SYNC_FLUSH);
if (result != Z_OK) {
buf_setlen(ret, ret->size - zstream->avail_out);
buf_setpos(ret, ret->len);
if (result != Z_BUF_ERROR && result != Z_OK) {
dropbear_exit("zlib error"); dropbear_exit("zlib error");
} }
if (zstream->avail_in == 0 && buf_setlen(ret, ret->size - zstream->avail_out);
(zstream->avail_out != 0 || result == Z_BUF_ERROR)) {
/* we can only exit if avail_out hasn't all been used,
* and there's no remaining input */
return ret;
}
if (zstream->avail_out == 0) { if (zstream->avail_in > 0 || ret->len > RECV_MAX_PAYLOAD_LEN) {
int new_size = 0; /* The remote side sent larger than a payload size
if (ret->size >= RECV_MAX_PAYLOAD_LEN) { * of uncompressed data.
/* Already been increased as large as it can go, */
* yet didn't finish up the decompression */
dropbear_exit("bad packet, oversized decompressed"); dropbear_exit("bad packet, oversized decompressed");
} }
new_size = MIN(RECV_MAX_PAYLOAD_LEN, ret->size + ZLIB_DECOMPRESS_INCR);
ret = buf_resize(ret, new_size); /* Success. All input was consumed and avail_out > 0 */
} return ret;
}
} }
#endif #endif