CVE-2017-11665 (C, published 2017-07-27T12:29Z)
Description: value: The ff_amf_get_field_value function in libavformat/rtmppkt.c in FFmpeg 3.3.2 allows remote RTMP servers to cause a denial of service (Segmentation Violation and application crash) via a crafted stream.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-20 \u2014 Improper Input Validation
The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Repository: https://github.com/FFmpeg/FFmpeg
Files changed:
libavformat/rtmppkt.c
Commit message: avformat/rtmppkt: Convert ff_amf_get_field_value() to bytestream2

Fixes: out of array accesses

Found-by: JunDong Xie of Ant-financial Light-Year Security Lab
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>


Vulnerable code:
int ff_amf_get_field_value(const uint8_t *data, const uint8_t *data_end,
    while (*data != AMF_DATA_TYPE_OBJECT && data < data_end) {
        len = ff_amf_tag_size(data, data_end);
        if (len < 0)
            len = data_end - data;
        data += len;
    if (data_end - data < 3)
    data++;
        int size = bytestream_get_be16(&data);
        if (size < 0 || size >= data_end - data)
        data += size;
        if (size == namelen && !memcmp(data-size, name, namelen)) {
            switch (*data++) {
                snprintf(dst, dst_size, "%g", av_int2double(AV_RB64(data)));
                snprintf(dst, dst_size, "%s", *data ? "true" : "false");
                len = bytestream_get_be16(&data);
                av_strlcpy(dst, data, FFMIN(len+1, dst_size));
        len = ff_amf_tag_size(data, data_end);
        if (len < 0 || len >= data_end - data)
        data += len;

Fixed code:
static int amf_get_field_value2(GetByteContext *gb,
    while (bytestream2_peek_byte(gb) != AMF_DATA_TYPE_OBJECT && bytestream2_get_bytes_left(gb) > 0) {
        int ret = amf_tag_skip(gb);
        if (ret < 0)
            return -1;
    if (bytestream2_get_bytes_left(gb) < 3)
    bytestream2_get_byte(gb);

        int size = bytestream2_get_be16(gb);
        if (size < 0 || size >= bytestream2_get_bytes_left(gb))
        bytestream2_skip(gb, size);
        if (size == namelen && !memcmp(gb->buffer-size, name, namelen)) {
            switch (bytestream2_get_byte(gb)) {
                snprintf(dst, dst_size, "%g", av_int2double(bytestream2_get_be64(gb)));
                snprintf(dst, dst_size, "%s", bytestream2_get_byte(gb) ? "true" : "false");
                len = bytestream2_get_be16(gb);
                if (dst_size < 1)
                    return -1;
                if (dst_size < len + 1)
                    len = dst_size - 1;
                bytestream2_get_buffer(gb, dst, len);
                dst[len] = 0;
        len = amf_tag_skip(gb);
        if (len < 0 || bytestream2_get_bytes_left(gb) <= 0)
int ff_amf_get_field_value(const uint8_t *data, const uint8_t *data_end,
                           const uint8_t *name, uint8_t *dst, int dst_size)
{
    GetByteContext gb;

    if (data >= data_end)
        return -1;

    bytestream2_init(&gb, data, data_end - data);

    return amf_get_field_value2(&gb, name, dst, dst_size);
}