X-Debbugs-CC: mh...@suse.de, n...@debian.org Package: xine-lib Version: 1.1.14-3 Severity: serious Tag: security
Hi, so CVE-2008-5236 is Multiple heap-based buffer overflows in xine-lib 1.1.12, and other 1.1.15 and earlier versions, allow remote attackers to execute arbitrary code via vectors related to (1) a crafted EBML element length processed by the parse_block_group function in demux_matroska.c; (2) a certain combination of sps, w, and h values processed by the real_parse_audio_specific_data and demux_real_send_chunk functions in demux_real.c; and (3) an unspecified combination of three values processed by the open_ra_file function in demux_realaudio.c. NOTE: vector 2 reportedly exists because of an incomplete fix in 1.1.15. Attached are a patch by Matthias Hopf of SuSE (please credit him in the ChangeLog) and a "backport" to the Debian package including the required 1.1.14->1.1.15 chagnes as well. I should recommend to use xine-lib 1.1.15 alltogether (at least for the three files) to have a better basis for future security updates. So let's look at the three patched files: demux_matroska.c: The fix seems to be sane and protect against int overflow in malloc(sizeof(xine_bmiheader) + track->codec_private_len); followed by memcpy'ing track->codec_private_len bytes. Also, various size variables in xine (e.g. buf_element_s.size) that codec_private_len is assigned to in the code are still signed, so bounding the range seems like a good thing. I should note that there are still potential problems that could lead to (at least) a crash, e.g. the pointer returned by above malloc is not checked against NULL. demux_real.c: As the w and h parameters are later used in for loops, overflow of the product might lead to out of bounds memory writes as described in the advisory description. It would seem that setting the stream buffer to NULL results in later checks to prevent bad things from happening. I would like to point out that a lot of the variables used to calculate (positive) offsets are signed and wonder whether they also need to be checked for < 0 in order to forbid manipulation via that route. They are later multiplied, so that two negative signs could also lead to a positive offset (but negative ones might be bad, too). Also, I wonder whether producs other than w*h need to be taken into account. demux_realaudio.c: This seems to require a signedness change from 1.1.15 to work properly. Protecting w,h against overflow seems essential for much of the same reasons as above (also, like in the snipped below, they are still assigned to signed variables). Similar comments as for demux_real.c apply here, too, w.r.t. signedness of pos below and the possibilities overflowing other products. In particular, I wonder if the follwing (in function demux_ra_send_chunk) is a recipe for trouble. (But could be just me.) int x, y; int pos; for (y = 0; y < this->h; y++) for (x = 0; x < this->h / 2; x++) { pos = x * 2 * this->w + y * this->cfs; if(this->input->read(this->input, this->frame_buffer + pos, this->cfs) < this->cfs) { xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_realaudio: failed to read audio chunk\n"); Kind regards T. -- Thomas Viehmann, http://thomas.viehmann.net/
Patches by Matthias Hopf of SuSE backproted to Debian xine-lib 1.1.14-3. diff -u xine-lib-1.1.14/src/demuxers/demux_realaudio.c xine-lib-1.1.14/src/demuxers/demux_realaudio.c --- xine-lib-1.1.14/src/demuxers/demux_realaudio.c +++ xine-lib-1.1.14/src/demuxers/demux_realaudio.c @@ -64,7 +64,9 @@ off_t data_start; off_t data_size; - int sps, cfs, w, h; + uint32_t cfs; + uint16_t w, h; + int frame_len; size_t frame_size; uint8_t *frame_buffer; @@ -197,12 +199,18 @@ this->w = _X_BE_16 (this->header+42); this->h = _X_BE_16 (this->header+40); this->cfs = _X_BE_32 (this->header+24); - if (this->sps) { - this->frame_size = this->sps * this->h * this->sps; - this->frame_buffer = xine_xmalloc (this->frame_size); - } else { - this->frame_size = this->w * this->h; - this->frame_buffer = xine_xmalloc (this->frame_size); + if (this->w < 0x8000 && this->h < 0x8000) { + uint64_t fs; + this->frame_len = this->w * this->h; + fs = (uint64_t) this->frame_len * sps; + if (fs < 0x80000000) { + this->frame_size = fs; + this->frame_buffer = calloc(this->frame_size, 1); + } + } + if (! this->frame_buffer) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_realaudio: malloc failed\n"); + return 0; } if (this->audio_type == BUF_AUDIO_28_8 || this->audio_type == BUF_AUDIO_SIPRO) diff -u xine-lib-1.1.14/src/demuxers/demux_real.c xine-lib-1.1.14/src/demuxers/demux_real.c --- xine-lib-1.1.14/src/demuxers/demux_real.c +++ xine-lib-1.1.14/src/demuxers/demux_real.c @@ -365,15 +365,20 @@ "demux_real: error, i don't handle buf type 0x%08x\n", stream->buf_type); } - if (stream->sps) { - stream->frame_size = stream->w / stream->sps * stream->h * stream->sps; - stream->frame_buffer = xine_xmalloc (stream->frame_size); - stream->frame_num_bytes = 0; - } else { + /* + * when stream->sps is set it used to do this: + * stream->frame_size = stream->w / stream->sps * stream->h * stream->sps; + * but it looks pointless? the compiler will probably optimise it away, I suppose? + */ + if (stream->w < 32768 && stream->h < 32768) { stream->frame_size = stream->w * stream->h; - stream->frame_buffer = xine_xmalloc (stream->frame_size); - stream->frame_num_bytes = 0; + stream->frame_buffer = calloc(stream->frame_size, 1); + } else { + stream->frame_size = 0; + stream->frame_buffer = NULL; } + + stream->frame_num_bytes = 0; stream->sub_packet_cnt = 0; if (!stream->frame_buffer) diff -u xine-lib-1.1.14/src/demuxers/demux_matroska.c xine-lib-1.1.14/src/demuxers/demux_matroska.c --- xine-lib-1.1.14/src/demuxers/demux_matroska.c +++ xine-lib-1.1.14/src/demuxers/demux_matroska.c @@ -1179,7 +1179,12 @@ break; case MATROSKA_ID_TR_CODECPRIVATE: { - char *codec_private = malloc (elem.len); + char *codec_private; + if (elem.len >= 0x80000000) + return 0; + codec_private = malloc (elem.len); + if (! codec_private) + return 0; lprintf("CodecPrivate\n"); if (!ebml_read_binary(ebml, &elem, codec_private)) { free(codec_private);
# CVE 2008-5236 Multiple heap-based buffer overflows in xine-lib 1.1.12, and other 1.1.15 and earlier versions, allow remote attackers to execute arbitrary code via vectors related to (1) a crafted EBML element length processed by the parse_block_group function in demux_matroska.c; (2) a certain combination of sps, w, and h values processed by the real_parse_audio_specific_data and demux_real_send_chunk functions in demux_real.c; and (3) an unspecified combination of three values processed by the open_ra_file function in demux_realaudio.c. NOTE: vector 2 reportedly exists because of an incomplete fix in 1.1.15. diff -r f98dfc2d6b7a src/demuxers/demux_matroska.c --- a/src/demuxers/demux_matroska.c Thu Oct 02 17:31:31 2008 +0200 +++ b/src/demuxers/demux_matroska.c Wed Oct 08 14:51:04 2008 +0200 @@ -1179,7 +1183,12 @@ break; case MATROSKA_ID_TR_CODECPRIVATE: { - char *codec_private = malloc (elem.len); + char *codec_private; + if (elem.len >= 0x80000000) + return 0; + codec_private = malloc (elem.len); + if (! codec_private) + return 0; lprintf("CodecPrivate\n"); if (!ebml_read_binary(ebml, &elem, codec_private)) { free(codec_private); diff -r f98dfc2d6b7a src/demuxers/demux_real.c --- a/src/demuxers/demux_real.c Thu Oct 02 17:31:31 2008 +0200 +++ b/src/demuxers/demux_real.c Wed Oct 08 14:51:04 2008 +0200 @@ -359,9 +362,14 @@ * stream->frame_size = stream->w / stream->sps * stream->h * stream->sps; * but it looks pointless? the compiler will probably optimise it away, I suppose? */ - stream->frame_size = stream->w * stream->h; + if (stream->w < 32768 && stream->h < 32768) { + stream->frame_size = stream->w * stream->h; + stream->frame_buffer = calloc(stream->frame_size, 1); + } else { + stream->frame_size = 0; + stream->frame_buffer = NULL; + } - stream->frame_buffer = calloc(stream->frame_size, 1); stream->frame_num_bytes = 0; stream->sub_packet_cnt = 0; diff -r f98dfc2d6b7a src/demuxers/demux_realaudio.c --- a/src/demuxers/demux_realaudio.c Thu Oct 02 17:31:31 2008 +0200 +++ b/src/demuxers/demux_realaudio.c Wed Oct 08 14:51:04 2008 +0200 @@ -202,11 +202,19 @@ this->h = _X_BE_16 (this->header+40); this->cfs = _X_BE_32 (this->header+24); - this->frame_len = this->w * this->h; - this->frame_size = this->frame_len * sps; - - this->frame_buffer = calloc(this->frame_size, 1); - _x_assert(this->frame_buffer != NULL); + if (this->w < 0x8000 && this->h < 0x8000) { + uint64_t fs; + this->frame_len = this->w * this->h; + fs = (uint64_t) this->frame_len * sps; + if (fs < 0x80000000) { + this->frame_size = fs; + this->frame_buffer = calloc(this->frame_size, 1); + } + } + if (! this->frame_buffer) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_realaudio: malloc failed\n"); + return 0; + } if (this->audio_type == BUF_AUDIO_28_8 || this->audio_type == BUF_AUDIO_SIPRO) this->block_align = this->cfs;