Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time

Overview

libbpg is a library for the BPG graphics format. libbpg 0.9.7 may allow a crafted file to read out-of-bounds, which may lead to denial of service.

Tested Versions

libbpg 0.9.7

Product URLs

http://bellard.org/bpg/

Details

libbpg have an out-of-bounds read denial-of-service vulnerability when parsing a crafted BPG file. This occurs in the hls_pcm_sample function of the hevc.c source file when processing corrupted frames.
In hls_pcm_sample function, s->frame->data is an array to store frame data in BPG file. An attacker can controll BPG file to make s->frame->data has invalid value(eg. s->frame->data[1]=0) in the array, and crash in put_pcm function.

static int hls_pcm_sample(HEVCContext *s, int x0, int y0, int log2_cb_size)
{
    HEVCLocalContext *lc = s->HEVClc;
    GetBitContext gb;
    int cb_size   = 1 << log2_cb_size;
    int stride0   = s->frame->linesize[0];
    uint8_t *dst0 = &s->frame->data[0][y0 * stride0 + (x0 << s->sps->pixel_shift)];
    int   stride1 = s->frame->linesize[1];
    uint8_t *dst1 = &s->frame->data[1][(y0 >> s->sps->vshift[1]) * stride1 + ((x0 >> s->sps->hshift[1]) << s->sps->pixel_shift)];
    int   stride2 = s->frame->linesize[2];
    uint8_t *dst2 = &s->frame->data[2][(y0 >> s->sps->vshift[2]) * stride2 + ((x0 >> s->sps->hshift[2]) << s->sps->pixel_shift)];

    int length         = cb_size * cb_size * s->sps->pcm.bit_depth +
                         (((cb_size >> s->sps->hshift[1]) * (cb_size >> s->sps->vshift[1])) +
                          ((cb_size >> s->sps->hshift[2]) * (cb_size >> s->sps->vshift[2]))) *
                          s->sps->pcm.bit_depth_chroma;
    const uint8_t *pcm = skip_bytes(&lc->cc, (length + 7) >> 3);
    int ret;

    if (!s->sh.disable_deblocking_filter_flag)
        ff_hevc_deblocking_boundary_strengths(s, x0, y0, log2_cb_size);

    ret = init_get_bits(&gb, pcm, length);
    if (ret < 0)
        return ret;

    s->hevcdsp.put_pcm(dst0, stride0, cb_size, cb_size,     &gb, s->sps->pcm.bit_depth BIT_DEPTH_ARG2(s->sps->bit_depth));
    s->hevcdsp.put_pcm(dst1, stride1,
                       cb_size >> s->sps->hshift[1],
                       cb_size >> s->sps->vshift[1],
                       &gb, s->sps->pcm.bit_depth_chroma BIT_DEPTH_ARG2(s->sps->bit_depth));
    s->hevcdsp.put_pcm(dst2, stride2,
                       cb_size >> s->sps->hshift[2],
                       cb_size >> s->sps->vshift[2],
                       &gb, s->sps->pcm.bit_depth_chroma BIT_DEPTH_ARG2(s->sps->bit_depth));
    return 0;
}

put_pcm function will call FUNC(put_pcm) in hevcdsp_template.c

static void FUNC(put_pcm)(uint8_t *_dst, ptrdiff_t stride, int width, int height,
                          GetBitContext *gb, int pcm_bit_depth BIT_DEPTH_PARAM)
{
    int x, y;
    pixel *dst = (pixel *)_dst;

    stride /= sizeof(pixel);

    for (y = 0; y < height; y++) {
        for (x = 0; x < width; x++)
            dst[x] = get_bits(gb, pcm_bit_depth) << (BIT_DEPTH - pcm_bit_depth);
        dst += stride;
    }
}

Crash Information

The output of gdb

Reading symbols from bpgdec...done.
(gdb) set args libbpg-put_pcm_var-40.crash
(gdb) run
Starting program: /root/libbpg-0.9.7/bpgdec libbpg-put_pcm_var-40.crash
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x000000000040faa9 in put_pcm_var (_dst=<optimized out>, stride=<optimized out>, width=8, height=8, gb=<optimized out>, pcm_bit_depth=<optimized out>, bit_depth=8)
    at libavcodec/hevcdsp_template.c:40
40                  dst[x] = get_bits(gb, pcm_bit_depth) << (BIT_DEPTH - pcm_bit_depth);

(gdb) bt
#0  0x000000000040faa9 in put_pcm_var (_dst=<optimized out>, stride=<optimized out>, width=8, height=8, gb=<optimized out>, pcm_bit_depth=<optimized out>, bit_depth=8)
    at libavcodec/hevcdsp_template.c:40
#1  0x00000000004068d8 in hls_pcm_sample (log2_cb_size=3, y0=8, x0=288, s=0x627000018900) at libavcodec/hevc.c:1294
#2  hls_coding_unit (log2_cb_size=3, y0=8, x0=288, s=0x627000018900) at libavcodec/hevc.c:2120
#3  hls_coding_quadtree (s=s@entry=0x627000018900, x0=x0@entry=288, y0=y0@entry=8, log2_cb_size=3, cb_depth=cb_depth@entry=0) at libavcodec/hevc.c:2281
#4  0x0000000000407bb4 in hls_decode_entry (avctxt=<optimized out>, isFilterThread=<optimized out>) at libavcodec/hevc.c:2386
#5  0x000000000041361d in avcodec_default_execute (c=0x619000008780, func=0x407291 <hls_decode_entry>, arg=<optimized out>, ret=0x7fffffffe060, count=<optimized out>, size=4)
    at libavcodec/utils.c:121
#6  0x00000000004097f9 in hls_slice_data (s=0x627000018900) at libavcodec/hevc.c:2413
#7  decode_nal_unit (length=<optimized out>, nal=<optimized out>, s=0x627000018900) at libavcodec/hevc.c:2826
#8  decode_nal_units (length=<optimized out>, buf=<optimized out>, s=0x627000018900) at libavcodec/hevc.c:3063
#9  hevc_decode_frame (avctx=<optimized out>, data=0x61500000fd00, got_output=0x7fffffffe184, avpkt=0x7fffffffe0d8) at libavcodec/hevc.c:3193
#10 0x00000000004139ef in avcodec_decode_video2 (avctx=avctx@entry=0x619000008780, picture=picture@entry=0x61500000fd00, got_picture_ptr=got_picture_ptr@entry=0x7fffffffe184, 
    avpkt=avpkt@entry=0x7fffffffe188) at libavcodec/utils.c:242
#11 0x00000000004027e8 in hevc_write_frame (avctx=0x619000008780, frame=0x61500000fd00, buf=0x61d00001ea80 "", buf_len=1296) at libbpg.c:401
#12 0x0000000000402924 in hevc_decode_frame_internal (s=s@entry=0x61300000de80, abuf=abuf@entry=0x7fffffffe2b0, cbuf=cbuf@entry=0x7fffffffe2c0, buf=0x62100001cb3f "", 
    buf_len1=buf_len1@entry=4652, first_nal=<optimized out>, first_nal@entry=1) at libbpg.c:479
#13 0x00000000004040d4 in hevc_decode_start (has_alpha=<optimized out>, bit_depth=<optimized out>, chroma_format_idc=<optimized out>, height=<optimized out>, width=320, 
    buf_len1=<optimized out>, buf=<optimized out>, s=0x61300000de80) at libbpg.c:528
#14 bpg_decoder_decode (img=img@entry=0x61300000de80, buf=buf@entry=0x62100001b900 "BPG\373\060\004\202@\201p", buf_len=buf_len@entry=4671) at libbpg.c:1860
#15 0x00000000004017bf in main (argc=<optimized out>, argv=<optimized out>) at bpgdec.c:332

(gdb) f 1
#1  0x00000000004068d8 in hls_pcm_sample (log2_cb_size=3, y0=8, x0=288, s=0x627000018900) at libavcodec/hevc.c:1294
1294        s->hevcdsp.put_pcm(dst1, stride1,

(gdb) p s->frame->data
$1 = {0x7ffff7f4f800 "\200", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}

The output of bpgdec with address sanitizer enabled

./bpgdec libbpg-put_pcm_var-40.crash 
ASAN:SIGSEGV
=================================================================
==31574==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000240 (pc 0x00000042d6eb bp 0x000000000000 sp 0x7ffd7ac15e20 T0)
    #0 0x42d6ea in put_pcm_var libavcodec/hevcdsp_template.c:40

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV libavcodec/hevcdsp_template.c:40 put_pcm_var
==31574==ABORTING

POC file

For security reason, this file was encrypted. Researchers can communicate with me to get the password.
libbpg-put_pcm_var-40.zip

CREDIT

Zhao Liang, Huawei Weiran Labs