1
mirror of https://git.videolan.org/git/ffmpeg.git synced 2024-10-03 01:21:46 +02:00

wavpack: use bytestream2 in wavpack_decode_block

Prevent most out of buffer reads.

Reported-by: Mateusz "j00ru" Jurczyk and Gynvael Coldwind
CC: libav-stable@libav.org
This commit is contained in:
Luca Barbato 2013-05-22 12:27:04 +02:00
parent f36d7831d9
commit 3f0b6d7a62

View File

@ -26,6 +26,7 @@
#include "get_bits.h" #include "get_bits.h"
#include "internal.h" #include "internal.h"
#include "unary.h" #include "unary.h"
#include "bytestream.h"
/** /**
* @file * @file
@ -761,12 +762,11 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
{ {
WavpackContext *wc = avctx->priv_data; WavpackContext *wc = avctx->priv_data;
WavpackFrameContext *s; WavpackFrameContext *s;
GetByteContext gb;
void *samples = data; void *samples = data;
int samplecount; int samplecount;
int got_terms = 0, got_weights = 0, got_samples = 0, int got_terms = 0, got_weights = 0, got_samples = 0,
got_entropy = 0, got_bs = 0, got_float = 0, got_hybrid = 0; got_entropy = 0, got_bs = 0, got_float = 0, got_hybrid = 0;
const uint8_t *orig_buf = buf;
const uint8_t *buf_end = buf + buf_size;
int i, j, id, size, ssize, weights, t; int i, j, id, size, ssize, weights, t;
int bpp, chan, chmask, orig_bpp; int bpp, chan, chmask, orig_bpp;
@ -793,9 +793,10 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
s->and = s->or = s->shift = 0; s->and = s->or = s->shift = 0;
s->got_extra_bits = 0; s->got_extra_bits = 0;
bytestream2_init(&gb, buf, buf_size);
if (!wc->mkv_mode) { if (!wc->mkv_mode) {
s->samples = AV_RL32(buf); s->samples = bytestream2_get_le32(&gb);
buf += 4;
if (s->samples != wc->samples) if (s->samples != wc->samples)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
@ -806,8 +807,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
} else { } else {
s->samples = wc->samples; s->samples = wc->samples;
} }
s->frame_flags = AV_RL32(buf); s->frame_flags = bytestream2_get_le32(&gb);
buf += 4;
bpp = av_get_bytes_per_sample(avctx->sample_fmt); bpp = av_get_bytes_per_sample(avctx->sample_fmt);
samples = (uint8_t *)samples + bpp * wc->ch_offset; samples = (uint8_t *)samples + bpp * wc->ch_offset;
orig_bpp = ((s->frame_flags & 0x03) + 1) << 3; orig_bpp = ((s->frame_flags & 0x03) + 1) << 3;
@ -820,20 +820,20 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
s->post_shift = bpp * 8 - orig_bpp + ((s->frame_flags >> 13) & 0x1f); s->post_shift = bpp * 8 - orig_bpp + ((s->frame_flags >> 13) & 0x1f);
s->hybrid_maxclip = ((1LL << (orig_bpp - 1)) - 1); s->hybrid_maxclip = ((1LL << (orig_bpp - 1)) - 1);
s->hybrid_minclip = ((-1LL << (orig_bpp - 1))); s->hybrid_minclip = ((-1LL << (orig_bpp - 1)));
s->CRC = AV_RL32(buf); s->CRC = bytestream2_get_le32(&gb);
buf += 4;
if (wc->mkv_mode) if (wc->mkv_mode)
buf += 4; // skip block size; bytestream2_skip(&gb, 4); // skip block size;
wc->ch_offset += 1 + s->stereo; wc->ch_offset += 1 + s->stereo;
// parse metadata blocks // parse metadata blocks
while (buf < buf_end) { while (bytestream2_get_bytes_left(&gb)) {
id = *buf++; id = bytestream2_get_byte(&gb);
size = *buf++; size = bytestream2_get_byte(&gb);
if (id & WP_IDF_LONG) { if (id & WP_IDF_LONG) {
size |= (*buf++) << 8; size |= (bytestream2_get_byte(&gb)) << 8;
size |= (*buf++) << 16; size |= (bytestream2_get_byte(&gb)) << 16;
} }
size <<= 1; // size is specified in words size <<= 1; // size is specified in words
ssize = size; ssize = size;
@ -844,13 +844,13 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
"Got incorrect block %02X with size %i\n", id, size); "Got incorrect block %02X with size %i\n", id, size);
break; break;
} }
if (buf + ssize > buf_end) { if (bytestream2_get_bytes_left(&gb) < ssize) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Block size %i is out of bounds\n", size); "Block size %i is out of bounds\n", size);
break; break;
} }
if (id & WP_IDF_IGNORE) { if (id & WP_IDF_IGNORE) {
buf += ssize; bytestream2_skip(&gb, ssize);
continue; continue;
} }
switch (id & WP_IDF_MASK) { switch (id & WP_IDF_MASK) {
@ -858,14 +858,14 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
if (size > MAX_TERMS) { if (size > MAX_TERMS) {
av_log(avctx, AV_LOG_ERROR, "Too many decorrelation terms\n"); av_log(avctx, AV_LOG_ERROR, "Too many decorrelation terms\n");
s->terms = 0; s->terms = 0;
buf += ssize; bytestream2_skip(&gb, ssize);
continue; continue;
} }
s->terms = size; s->terms = size;
for (i = 0; i < s->terms; i++) { for (i = 0; i < s->terms; i++) {
s->decorr[s->terms - i - 1].value = (*buf & 0x1F) - 5; uint8_t val = bytestream2_get_byte(&gb);
s->decorr[s->terms - i - 1].delta = *buf >> 5; s->decorr[s->terms - i - 1].value = (val & 0x1F) - 5;
buf++; s->decorr[s->terms - i - 1].delta = val >> 5;
} }
got_terms = 1; got_terms = 1;
break; break;
@ -877,17 +877,17 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
weights = size >> s->stereo_in; weights = size >> s->stereo_in;
if (weights > MAX_TERMS || weights > s->terms) { if (weights > MAX_TERMS || weights > s->terms) {
av_log(avctx, AV_LOG_ERROR, "Too many decorrelation weights\n"); av_log(avctx, AV_LOG_ERROR, "Too many decorrelation weights\n");
buf += ssize; bytestream2_skip(&gb, ssize);
continue; continue;
} }
for (i = 0; i < weights; i++) { for (i = 0; i < weights; i++) {
t = (int8_t)(*buf++); t = (int8_t)bytestream2_get_byte(&gb);
s->decorr[s->terms - i - 1].weightA = t << 3; s->decorr[s->terms - i - 1].weightA = t << 3;
if (s->decorr[s->terms - i - 1].weightA > 0) if (s->decorr[s->terms - i - 1].weightA > 0)
s->decorr[s->terms - i - 1].weightA += s->decorr[s->terms - i - 1].weightA +=
(s->decorr[s->terms - i - 1].weightA + 64) >> 7; (s->decorr[s->terms - i - 1].weightA + 64) >> 7;
if (s->stereo_in) { if (s->stereo_in) {
t = (int8_t)(*buf++); t = (int8_t)bytestream2_get_byte(&gb);
s->decorr[s->terms - i - 1].weightB = t << 3; s->decorr[s->terms - i - 1].weightB = t << 3;
if (s->decorr[s->terms - i - 1].weightB > 0) if (s->decorr[s->terms - i - 1].weightB > 0)
s->decorr[s->terms - i - 1].weightB += s->decorr[s->terms - i - 1].weightB +=
@ -904,31 +904,32 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
t = 0; t = 0;
for (i = s->terms - 1; (i >= 0) && (t < size); i--) { for (i = s->terms - 1; (i >= 0) && (t < size); i--) {
if (s->decorr[i].value > 8) { if (s->decorr[i].value > 8) {
s->decorr[i].samplesA[0] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesA[0] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
s->decorr[i].samplesA[1] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesA[1] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
if (s->stereo_in) { if (s->stereo_in) {
s->decorr[i].samplesB[0] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesB[0] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
s->decorr[i].samplesB[1] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesB[1] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
t += 4; t += 4;
} }
t += 4; t += 4;
} else if (s->decorr[i].value < 0) { } else if (s->decorr[i].value < 0) {
s->decorr[i].samplesA[0] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesA[0] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
s->decorr[i].samplesB[0] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesB[0] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
t += 4; t += 4;
} else { } else {
for (j = 0; j < s->decorr[i].value; j++) { for (j = 0; j < s->decorr[i].value; j++) {
s->decorr[i].samplesA[j] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesA[j] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
if (s->stereo_in) { if (s->stereo_in) {
s->decorr[i].samplesB[j] = wp_exp2(AV_RL16(buf)); s->decorr[i].samplesB[j] =
buf += 2; wp_exp2(bytestream2_get_le16(&gb));
} }
} }
t += s->decorr[i].value * 2 * (s->stereo_in + 1); t += s->decorr[i].value * 2 * (s->stereo_in + 1);
@ -941,33 +942,30 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Entropy vars size should be %i, got %i", "Entropy vars size should be %i, got %i",
6 * (s->stereo_in + 1), size); 6 * (s->stereo_in + 1), size);
buf += ssize; bytestream2_skip(&gb, ssize);
continue; continue;
} }
for (j = 0; j <= s->stereo_in; j++) for (j = 0; j <= s->stereo_in; j++)
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
s->ch[j].median[i] = wp_exp2(AV_RL16(buf)); s->ch[j].median[i] = wp_exp2(bytestream2_get_le16(&gb));
buf += 2;
} }
got_entropy = 1; got_entropy = 1;
break; break;
case WP_ID_HYBRID: case WP_ID_HYBRID:
if (s->hybrid_bitrate) { if (s->hybrid_bitrate) {
for (i = 0; i <= s->stereo_in; i++) { for (i = 0; i <= s->stereo_in; i++) {
s->ch[i].slow_level = wp_exp2(AV_RL16(buf)); s->ch[i].slow_level = wp_exp2(bytestream2_get_le16(&gb));
buf += 2;
size -= 2; size -= 2;
} }
} }
for (i = 0; i < (s->stereo_in + 1); i++) { for (i = 0; i < (s->stereo_in + 1); i++) {
s->ch[i].bitrate_acc = AV_RL16(buf) << 16; s->ch[i].bitrate_acc = bytestream2_get_le16(&gb) << 16;
buf += 2;
size -= 2; size -= 2;
} }
if (size > 0) { if (size > 0) {
for (i = 0; i < (s->stereo_in + 1); i++) { for (i = 0; i < (s->stereo_in + 1); i++) {
s->ch[i].bitrate_delta = wp_exp2((int16_t)AV_RL16(buf)); s->ch[i].bitrate_delta =
buf += 2; wp_exp2((int16_t)bytestream2_get_le16(&gb));
} }
} else { } else {
for (i = 0; i < (s->stereo_in + 1); i++) for (i = 0; i < (s->stereo_in + 1); i++)
@ -975,24 +973,26 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
} }
got_hybrid = 1; got_hybrid = 1;
break; break;
case WP_ID_INT32INFO: case WP_ID_INT32INFO: {
uint8_t val[4];
if (size != 4) { if (size != 4) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Invalid INT32INFO, size = %i, sent_bits = %i\n", "Invalid INT32INFO, size = %i\n",
size, *buf); size);
buf += ssize; bytestream2_skip(&gb, ssize - 4);
continue; continue;
} }
if (buf[0]) bytestream2_get_buffer(&gb, val, 4);
s->extra_bits = buf[0]; if (val[0]) {
else if (buf[1]) s->extra_bits = val[0];
s->shift = buf[1]; } else if (val[1]) {
else if (buf[2]) { s->shift = val[1];
} else if (val[2]) {
s->and = s->or = 1; s->and = s->or = 1;
s->shift = buf[2]; s->shift = val[2];
} else if (buf[3]) { } else if (val[3]) {
s->and = 1; s->and = 1;
s->shift = buf[3]; s->shift = val[3];
} }
/* original WavPack decoder forces 32-bit lossy sound to be treated /* original WavPack decoder forces 32-bit lossy sound to be treated
* as 24-bit one in order to have proper clipping */ * as 24-bit one in order to have proper clipping */
@ -1002,41 +1002,41 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
s->hybrid_maxclip >>= 8; s->hybrid_maxclip >>= 8;
s->hybrid_minclip >>= 8; s->hybrid_minclip >>= 8;
} }
buf += 4;
break; break;
}
case WP_ID_FLOATINFO: case WP_ID_FLOATINFO:
if (size != 4) { if (size != 4) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Invalid FLOATINFO, size = %i\n", size); "Invalid FLOATINFO, size = %i\n", size);
buf += ssize; bytestream2_skip(&gb, ssize);
continue; continue;
} }
s->float_flag = buf[0]; s->float_flag = bytestream2_get_byte(&gb);
s->float_shift = buf[1]; s->float_shift = bytestream2_get_byte(&gb);
s->float_max_exp = buf[2]; s->float_max_exp = bytestream2_get_byte(&gb);
buf += 4;
got_float = 1; got_float = 1;
bytestream2_skip(&gb, 1);
break; break;
case WP_ID_DATA: case WP_ID_DATA:
s->sc.offset = buf - orig_buf; s->sc.offset = bytestream2_tell(&gb);
s->sc.size = size * 8; s->sc.size = size * 8;
init_get_bits(&s->gb, buf, size * 8); init_get_bits(&s->gb, gb.buffer, size * 8);
s->data_size = size * 8; s->data_size = size * 8;
buf += size; bytestream2_skip(&gb, size);
got_bs = 1; got_bs = 1;
break; break;
case WP_ID_EXTRABITS: case WP_ID_EXTRABITS:
if (size <= 4) { if (size <= 4) {
av_log(avctx, AV_LOG_ERROR, "Invalid EXTRABITS, size = %i\n", av_log(avctx, AV_LOG_ERROR, "Invalid EXTRABITS, size = %i\n",
size); size);
buf += size; bytestream2_skip(&gb, size);
continue; continue;
} }
s->extra_sc.offset = buf - orig_buf; s->extra_sc.offset = bytestream2_tell(&gb);
s->extra_sc.size = size * 8; s->extra_sc.size = size * 8;
init_get_bits(&s->gb_extra_bits, buf, size * 8); init_get_bits(&s->gb_extra_bits, gb.buffer, size * 8);
s->crc_extra_bits = get_bits_long(&s->gb_extra_bits, 32); s->crc_extra_bits = get_bits_long(&s->gb_extra_bits, 32);
buf += size; bytestream2_skip(&gb, size);
s->got_extra_bits = 1; s->got_extra_bits = 1;
break; break;
case WP_ID_CHANINFO: case WP_ID_CHANINFO:
@ -1045,23 +1045,24 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
"Insufficient channel information\n"); "Insufficient channel information\n");
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
} }
chan = *buf++; chan = bytestream2_get_byte(&gb);
switch (size - 2) { switch (size - 2) {
case 0: case 0:
chmask = *buf; chmask = bytestream2_get_byte(&gb);
break; break;
case 1: case 1:
chmask = AV_RL16(buf); chmask = bytestream2_get_le16(&gb);
break; break;
case 2: case 2:
chmask = AV_RL24(buf); chmask = bytestream2_get_le24(&gb);
break; break;
case 3: case 3:
chmask = AV_RL32(buf); chmask = bytestream2_get_le32(&gb);;
break; break;
case 5: case 5:
chan |= (buf[1] & 0xF) << 8; bytestream2_skip(&gb, 1);
chmask = AV_RL24(buf + 2); chan |= (bytestream2_get_byte(&gb) & 0xF) << 8;
chmask = bytestream2_get_le16(&gb);
break; break;
default: default:
av_log(avctx, AV_LOG_ERROR, "Invalid channel info size %d\n", av_log(avctx, AV_LOG_ERROR, "Invalid channel info size %d\n",
@ -1078,13 +1079,12 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
} }
if (!avctx->channel_layout) if (!avctx->channel_layout)
avctx->channel_layout = chmask; avctx->channel_layout = chmask;
buf += size - 1;
break; break;
default: default:
buf += size; bytestream2_skip(&gb, size);
} }
if (id & WP_IDF_ODD) if (id & WP_IDF_ODD)
buf++; bytestream2_skip(&gb, 1);
} }
if (!got_terms) { if (!got_terms) {