Commit message: avformat/asfdec_o: Check size_bmp more fully

Fixes: integer overflow and out of array access
Fixes: asfo-crash-46080c4341572a7137a162331af77f6ded45cbd7

Found-by: Paul Ch <paulcher@icloud.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
CVE: CVE-2018-1999011
CWE: CWE-119
Vulnerability description: FFmpeg before commit 2b46ebdbff1d8dec7a3d8ea280a612b91a582869 contains a Buffer Overflow vulnerability in asf_o format demuxer that can result in heap-buffer-overflow that may result in remote code execution. This attack appears to be exploitable via specially crafted ASF file that has to be provided as input to FFmpeg. This vulnerability appears to have been fixed in 2b46ebdbff1d8dec7a3d8ea280a612b91a582869 and later.

Project: FFmpeg
File: asfdec_o.c

static int asf_deinterleave(AVFormatContext *s, ASFPacket *asf_pkt, int st_num)
{
    ASFContext *asf    = s->priv_data;
    ASFStream *asf_st  = asf->asf_st[st_num];
    unsigned char *p   = asf_pkt->avpkt.data;
    uint16_t pkt_len   = asf->asf_st[st_num]->virtual_pkt_len;
    uint16_t chunk_len = asf->asf_st[st_num]->virtual_chunk_len;
    int nchunks        = pkt_len / chunk_len;
    AVPacket pkt;
    int pos = 0, j, l, ret;


    if ((ret = av_new_packet(&pkt, asf_pkt->data_size)) < 0)
        return ret;

    while (asf_pkt->data_size >= asf_st->span * pkt_len + pos) {
        if (pos >= asf_pkt->data_size) {
            break;
        }
        for (l = 0; l < pkt_len; l++) {
            if (pos >= asf_pkt->data_size) {
                break;
            }
            for (j = 0; j < asf_st->span; j++) {
                if ((pos + chunk_len) >= asf_pkt->data_size)
                    break;
                memcpy(pkt.data + pos,
                       p + (j * nchunks + l) * chunk_len,
                       chunk_len);
                pos += chunk_len;
            }
        }
        p += asf_st->span * pkt_len;
        if (p > asf_pkt->avpkt.data + asf_pkt->data_size)
            break;
    }
    av_packet_unref(&asf_pkt->avpkt);
    asf_pkt->avpkt = pkt;

    return 0;
}

Project: FFmpeg

static int asf_read_ext_content(AVFormatContext *s, const GUIDParseTable *g)
{
    ASFContext *asf  = s->priv_data;
    AVIOContext *pb  = s->pb;
    uint64_t size    = avio_rl64(pb);
    uint16_t nb_desc = avio_rl16(pb);
    int i, ret;

    for (i = 0; i < nb_desc; i++) {
        uint16_t name_len, type, val_len;
        uint8_t *name = NULL;

        name_len = avio_rl16(pb);
        if (!name_len)
            return AVERROR_INVALIDDATA;
        name = av_malloc(name_len);
        if (!name)
            return AVERROR(ENOMEM);
        avio_get_str16le(pb, name_len, name,
                         name_len);
        type    = avio_rl16(pb);
        if (type == ASF_BOOL)
            type = ASF_DWORD;
        val_len = avio_rl16(pb);

        ret = process_metadata(s, name, name_len, val_len, type, &s->metadata);
        av_freep(&name);
        if (ret < 0)
            return ret;
    }

    align_position(pb, asf->offset, size);
    return 0;
}

Project: FFmpeg

static int asf_read_header(AVFormatContext *s)
{
    ASFContext *asf         = s->priv_data;
    AVIOContext *pb         = s->pb;
    const GUIDParseTable *g = NULL;
    ff_asf_guid guid;
    int i, ret;
    uint64_t size;

    asf->preroll         = 0;
    asf->is_simple_index = 0;
    ff_get_guid(pb, &guid);
    if (ff_guidcmp(&guid, &ff_asf_header))
        return AVERROR_INVALIDDATA;
    avio_skip(pb, 8); // skip header object size
    avio_skip(pb, 6); // skip number of header objects and 2 reserved bytes
    asf->data_reached = 0;

    /* 1  is here instead of pb->eof_reached because (when not streaming), Data are skipped
     * for the first time,
     * Index object is processed and got eof and then seeking back to the Data is performed.
     */
    while (1) {
        if (avio_tell(pb) == asf->offset)
            break;
        asf->offset = avio_tell(pb);
        if ((ret = ff_get_guid(pb, &guid)) < 0) {
            if (ret == AVERROR_EOF && asf->data_reached)
                break;
            else
                goto failed;
        }
        g = find_guid(guid);
        if (g) {
            asf->unknown_offset = asf->offset;
            asf->is_header = 1;
            if ((ret = g->read_object(s, g)) < 0)
                goto failed;
        } else {
            size = avio_rl64(pb);
            align_position(pb, asf->offset, size);
        }
        if (asf->data_reached &&
            (!(pb->seekable & AVIO_SEEKABLE_NORMAL) ||
             (asf->b_flags & ASF_FLAG_BROADCAST)))
            break;
    }

    if (!asf->data_reached) {
        av_log(s, AV_LOG_ERROR, "Data Object was not found.\n");
        ret = AVERROR_INVALIDDATA;
        goto failed;
    }
    if (pb->seekable & AVIO_SEEKABLE_NORMAL)
        avio_seek(pb, asf->first_packet_offset, SEEK_SET);

    for (i = 0; i < asf->nb_streams; i++) {
        const char *rfc1766 = asf->asf_sd[asf->asf_st[i]->lang_idx].langs;
        AVStream *st        = s->streams[asf->asf_st[i]->index];
        set_language(s, rfc1766, &st->metadata);
    }

    for (i = 0; i < ASF_MAX_STREAMS; i++) {
        AVStream *st = NULL;

        st = find_stream(s, i);
        if (st) {
            av_dict_copy(&st->metadata, asf->asf_sd[i].asf_met, AV_DICT_IGNORE_SUFFIX);
            if (asf->asf_sd[i].aspect_ratio.num > 0 && asf->asf_sd[i].aspect_ratio.den > 0) {
                st->sample_aspect_ratio.num = asf->asf_sd[i].aspect_ratio.num;
                st->sample_aspect_ratio.den = asf->asf_sd[i].aspect_ratio.den;
            }
        }
    }

    return 0;

failed:
    asf_read_close(s);
    return ret;
}

Project: FFmpeg
File: asfdec_o.c

static int asf_read_language_list(AVFormatContext *s, const GUIDParseTable *g)
{
    ASFContext *asf   = s->priv_data;
    AVIOContext *pb   = s->pb;
    int i, ret;
    uint64_t size     = avio_rl64(pb);
    uint16_t nb_langs = avio_rl16(pb);

    if (nb_langs < ASF_MAX_STREAMS) {
        for (i = 0; i < nb_langs; i++) {
            size_t len;
            len = avio_r8(pb);
            if (!len)
                len = 6;
            if ((ret = get_asf_string(pb, len, asf->asf_sd[i].langs,
                                      sizeof(asf->asf_sd[i].langs))) < 0) {
                return ret;
            }
        }
    }

    align_position(pb, asf->offset, size);
    return 0;
}

Project: FFmpeg
File: asfdec_o.c

static int asf_read_payload(AVFormatContext *s, AVPacket *pkt)
{
    ASFContext *asf = s->priv_data;
    AVIOContext *pb = s->pb;
    int ret, i;
    ASFPacket *asf_pkt = NULL;

    if (!asf->sub_left) {
        uint32_t off_len, media_len;
        uint8_t stream_num;

        stream_num = avio_r8(pb);
        asf->stream_index = stream_num & ASF_STREAM_NUM;
        for (i = 0; i < asf->nb_streams; i++) {
            if (asf->stream_index == asf->asf_st[i]->stream_index) {
                asf_pkt               = &asf->asf_st[i]->pkt;
                asf_pkt->stream_index = asf->asf_st[i]->index;
                break;
            }
        }
        if (!asf_pkt) {
            if (asf->packet_offset + asf->packet_size <= asf->data_offset + asf->data_size) {
                if (!asf->packet_size) {
                    av_log(s, AV_LOG_ERROR, "Invalid packet size 0.\n");
                    return AVERROR_INVALIDDATA;
                }
                avio_seek(pb, asf->packet_offset + asf->packet_size, SEEK_SET);
                av_log(s, AV_LOG_WARNING, "Skipping the stream with the invalid stream index %d.\n",
                       asf->stream_index);
                return AVERROR(EAGAIN);
            } else
                return AVERROR_INVALIDDATA;
        }

        if (stream_num >> 7)
            asf_pkt->flags |= AV_PKT_FLAG_KEY;
        READ_LEN(asf->prop_flags & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE,
                 ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_, media_len);
        READ_LEN(asf->prop_flags & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE,
                 ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_, off_len);
        READ_LEN(asf->prop_flags & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE,
                 ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_, asf->rep_data_len);
        if (asf_pkt->size_left && (asf_pkt->frame_num != media_len)) {
            av_log(s, AV_LOG_WARNING, "Unfinished frame will be ignored\n");
            reset_packet(asf_pkt);
        }
        asf_pkt->frame_num = media_len;
        asf->sub_dts = off_len;
        if (asf->nb_mult_left) {
            if ((ret = asf_read_multiple_payload(s, pkt, asf_pkt)) < 0)
                return ret;
        } else if (asf->rep_data_len == 1) {
            asf->sub_left = 1;
            asf->state    = READ_SINGLE;
            pkt->flags    = asf_pkt->flags;
            if ((ret = asf_read_subpayload(s, pkt, 1)) < 0)
                return ret;
        } else {
            if ((ret = asf_read_single_payload(s, asf_pkt)) < 0)
                return ret;
        }
    } else {
        for (i = 0; i <= asf->nb_streams; i++) {
            if (asf->stream_index == asf->asf_st[i]->stream_index) {
                asf_pkt = &asf->asf_st[i]->pkt;
                break;
            }
        }
        if (!asf_pkt)
            return AVERROR_INVALIDDATA;
        pkt->flags         = asf_pkt->flags;
        pkt->dts           = asf_pkt->dts;
        pkt->stream_index  = asf->asf_st[i]->index;
        if ((ret = asf_read_subpayload(s, pkt, 0)) < 0) // read subpayload without its header
            return ret;
    }

    return 0;
}

Project: FFmpeg
File: asfdec_o.c

static int get_asf_string(AVIOContext *pb, int maxlen, char *buf, int buflen)
{
    char *q = buf;
    int ret = 0;
    if (buflen <= 0)
        return AVERROR(EINVAL);
    while (ret + 1 < maxlen) {
        uint8_t tmp;
        uint32_t ch;
        GET_UTF16(ch, (ret += 2) <= maxlen ? avio_rl16(pb) : 0, break;);
        PUT_UTF8(ch, tmp, if (q - buf < buflen - 1) *q++ = tmp;)
    }
    *q = 0;

    return ret;
}