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. An integer underflow vulnerability exists in the decoding of BPG images in libbpg library. A crafted BPG image decoded by libbpg can cause a denial of service and application crash.

Tested Versions

libbpg 0.9.7

Product URLs

http://bellard.org/bpg/

Details

in the for loop of sao_filter_CTB function, it will counter width and height and call copy_CTB_to_hv to copy data.
width=FFMIN(ctb_size_h, (s->sps->width >> s->sps->hshift[c_idx]) - x0)
so in some conditions, eg. s->sps->width==0, width will be -x0, a negative value.

#define CTB(tab, x, y) ((tab)[(y) * s->sps->ctb_width + (x)])

static void sao_filter_CTB(HEVCContext *s, int x, int y)
{
    int c_idx, c_count;
    int edges[4];  // 0 left 1 top 2 right 3 bottom
    int x_ctb                = x >> s->sps->log2_ctb_size;
    int y_ctb                = y >> s->sps->log2_ctb_size;
    int ctb_addr_rs          = y_ctb * s->sps->ctb_width + x_ctb;
    int ctb_addr_ts          = s->pps->ctb_addr_rs_to_ts[ctb_addr_rs];
    SAOParams *sao           = &CTB(s->sao, x_ctb, y_ctb);
    // flags indicating unfilterable edges
    uint8_t vert_edge[]      = { 0, 0 };
    uint8_t horiz_edge[]     = { 0, 0 };
    uint8_t diag_edge[]      = { 0, 0, 0, 0 };
    uint8_t lfase            = CTB(s->filter_slice_edges, x_ctb, y_ctb);
    uint8_t no_tile_filter   = s->pps->tiles_enabled_flag &&
                               !s->pps->loop_filter_across_tiles_enabled_flag;
    uint8_t restore          = no_tile_filter || !lfase;
    uint8_t left_tile_edge   = 0;
    uint8_t right_tile_edge  = 0;
    uint8_t up_tile_edge     = 0;
    uint8_t bottom_tile_edge = 0;

    edges[0]   = x_ctb == 0;
    edges[1]   = y_ctb == 0;
    edges[2]   = x_ctb == s->sps->ctb_width  - 1;
    edges[3]   = y_ctb == s->sps->ctb_height - 1;

    if (restore) {
        ... ...
    }
    
    c_count = (s->sps->chroma_format_idc != 0) ? 3 : 1;
    for (c_idx = 0; c_idx < c_count; c_idx++) {
        int x0       = x >> s->sps->hshift[c_idx];
        int y0       = y >> s->sps->vshift[c_idx];
        int stride_src = s->frame->linesize[c_idx];
        int ctb_size_h = (1 << (s->sps->log2_ctb_size)) >> s->sps->hshift[c_idx];
        int ctb_size_v = (1 << (s->sps->log2_ctb_size)) >> s->sps->vshift[c_idx];
        int width    = FFMIN(ctb_size_h, (s->sps->width  >> s->sps->hshift[c_idx]) - x0);
        int height   = FFMIN(ctb_size_v, (s->sps->height >> s->sps->vshift[c_idx]) - y0);
        uint8_t *src = &s->frame->data[c_idx][y0 * stride_src + (x0 << s->sps->pixel_shift)];
#if defined(USE_SAO_SMALL_BUFFER)
        int stride_dst = ((1 << (s->sps->log2_ctb_size)) + 2) << s->sps->pixel_shift;
        uint8_t *dst = s->sao_pixel_buffer + (1 * stride_dst) + (1 << s->sps->pixel_shift);
#else
        int stride_dst = s->sao_frame->linesize[c_idx];
        uint8_t *dst = &s->sao_frame->data[c_idx][y0 * stride_dst + (x0 << s->sps->pixel_shift)];
#endif

        switch (sao->type_idx[c_idx]) {
        case SAO_BAND:
            copy_CTB(dst, src, width << s->sps->pixel_shift, height, stride_dst, stride_src);
#if defined(USE_SAO_SMALL_BUFFER)
            copy_CTB_to_hv(s, src, stride_src, x0, y0, width, height, c_idx,
                           x_ctb, y_ctb);
#endif
            s->hevcdsp.sao_band_filter(src, dst,
                                       stride_src, stride_dst,
                                       sao,
                                       edges, width,
                                       height, c_idx BIT_DEPTH_ARG2(s->sps->bit_depth));
            restore_tqb_pixels(s, src, dst, stride_src, stride_dst,
                               x, y, width, height, c_idx);
            sao->type_idx[c_idx] = SAO_APPLIED;
            break;
        case SAO_EDGE:
        {
            ... ...
        }
        }
    }
}

then copy_CTB_to_hv function will be called with negative width, the function will call memcpy, with the third argument width << sh is negative too, so this underflow will trigger a segmentation fault.

static void copy_CTB_to_hv(HEVCContext *s, const uint8_t *src,
                           int stride_src, int x, int y, int width, int height,
                           int c_idx, int x_ctb, int y_ctb)
{
    int sh = s->sps->pixel_shift;
    int w = s->sps->width >> s->sps->hshift[c_idx];
    int h = s->sps->height >> s->sps->vshift[c_idx];

    /* copy horizontal edges */
    memcpy(s->sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << sh),
        src, width << sh);
    memcpy(s->sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 1) * w + x) << sh),
        src + stride_src * (height - 1), width << sh);

    /* copy vertical edges */
    copy_vert(s->sao_pixel_buffer_v[c_idx] + (((2 * x_ctb) * h + y) << sh), src, sh, height, 1 << sh, stride_src);

    copy_vert(s->sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 1) * h + y) << sh), src + ((width - 1) << sh), sh, height, 1 << sh, stride_src);
}

Crash Information

The output of gdb

Reading symbols from bpgdec...done.
(gdb) set args libbpg-sao_band_filter_0_var-351.crash
(gdb) run
Starting program: /root/libbpg-0.9.7/bpgdec libbpg-sao_band_filter_0_var-351.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.
copy_CTB_to_hv (s=s@entry=0x61ea00, src=src@entry=0x664250 "", stride_src=stride_src@entry=320, x=x@entry=96, y=y@entry=128, width=width@entry=-96, height=-128, c_idx=2, 
    x_ctb=3, y_ctb=2) at libavcodec/hevc_filter.c:193
193         memcpy(s->sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << sh), 

(gdb) bt
#0  copy_CTB_to_hv (s=s@entry=0x61ea00, src=src@entry=0x664250 "", stride_src=stride_src@entry=320, x=x@entry=96, y=y@entry=128, width=width@entry=-96, height=-128, c_idx=2, 
    x_ctb=3, y_ctb=2) at libavcodec/hevc_filter.c:193
#1  0x0000000000417b24 in sao_filter_CTB (s=s@entry=0x61ea00, x=x@entry=192, y=y@entry=128) at libavcodec/hevc_filter.c:320
#2  0x00000000004193cc in ff_hevc_hls_filter (s=s@entry=0x61ea00, x=x@entry=256, y=y@entry=192, ctb_size=ctb_size@entry=64) at libavcodec/hevc_filter.c:890
#3  0x0000000000407c43 in hls_decode_entry (avctxt=<optimized out>, isFilterThread=<optimized out>) at libavcodec/hevc.c:2400
#4  0x000000000041361d in avcodec_default_execute (c=0x61c1b0, func=0x407291 <hls_decode_entry>, arg=<optimized out>, ret=0x7fffffffe080, count=<optimized out>, size=4)
    at libavcodec/utils.c:121
#5  0x00000000004097f9 in hls_slice_data (s=0x61ea00) at libavcodec/hevc.c:2413
#6  decode_nal_unit (length=<optimized out>, nal=<optimized out>, s=0x61ea00) at libavcodec/hevc.c:2826
#7  decode_nal_units (length=<optimized out>, buf=<optimized out>, s=0x61ea00) at libavcodec/hevc.c:3063
#8  hevc_decode_frame (avctx=<optimized out>, data=0x61c610, got_output=0x7fffffffe1a4, avpkt=0x7fffffffe0f8) at libavcodec/hevc.c:3193
#9  0x00000000004139ef in avcodec_decode_video2 (avctx=avctx@entry=0x61c1b0, picture=picture@entry=0x61c610, got_picture_ptr=got_picture_ptr@entry=0x7fffffffe1a4, 
    avpkt=avpkt@entry=0x7fffffffe1a8) at libavcodec/utils.c:242
#10 0x00000000004027e8 in hevc_write_frame (avctx=0x61c1b0, frame=0x61c610, buf=0x62d300 "", buf_len=6052) at libbpg.c:401
#11 0x0000000000402954 in hevc_decode_frame_internal (s=s@entry=0x61c010, abuf=abuf@entry=0x7fffffffe2d0, cbuf=cbuf@entry=0x7fffffffe2e0, buf=0x61e9eb "", 
    buf_len1=buf_len1@entry=6028, first_nal=<optimized out>, first_nal@entry=1) at libbpg.c:486
#12 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=0x61c010) at libbpg.c:528
#13 bpg_decoder_decode (img=img@entry=0x61c010, buf=buf@entry=0x61d250 "BP", <incomplete sequence \373\240>, buf_len=buf_len@entry=6043) at libbpg.c:1860
#14 0x00000000004017bf in main (argc=<optimized out>, argv=<optimized out>) at bpgdec.c:332

(gdb) l
188         int sh = s->sps->pixel_shift;
189         int w = s->sps->width >> s->sps->hshift[c_idx];
190         int h = s->sps->height >> s->sps->vshift[c_idx];
191
192         /* copy horizontal edges */
193         memcpy(s->sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << sh), 
194             src, width << sh);
195         memcpy(s->sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 1) * w + x) << sh), 
196             src + stride_src * (height - 1), width << sh);
197         

(gdb) p width << sh
$1 = -192

(gdb) f 1
#1  0x0000000000417b24 in sao_filter_CTB (s=s@entry=0x61ea00, x=x@entry=192, y=y@entry=128) at libavcodec/hevc_filter.c:320
320                 copy_CTB_to_hv(s, src, stride_src, x0, y0, width, height, c_idx,

(gdb) l
315
316             switch (sao->type_idx[c_idx]) {
317             case SAO_BAND:
318                 copy_CTB(dst, src, width << s->sps->pixel_shift, height, stride_dst, stride_src);
319     #if defined(USE_SAO_SMALL_BUFFER)
320                 copy_CTB_to_hv(s, src, stride_src, x0, y0, width, height, c_idx,
321                                x_ctb, y_ctb);
322     #endif
323                 s->hevcdsp.sao_band_filter(src, dst,
324                                            stride_src, stride_dst,

(gdb) p width
$2 = -96
(gdb) 

The output of bpgdec with address sanitizer enabled

./bpgdec libbpg-sao_band_filter_0_var-351.crash 
ASAN:SIGSEGV
=================================================================
==51051==ERROR: AddressSanitizer: stack-overflow on address 0x7fffd75299dc (pc 0x000000426efa bp 0x000000000006 sp 0x7fffd7523a40 T0)
    #0 0x426ef9 in sao_band_filter_0_var libavcodec/hevcdsp_template.c:351

SUMMARY: AddressSanitizer: stack-overflow libavcodec/hevcdsp_template.c:351 sao_band_filter_0_var
==51051==ABORTING

POC file

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

CREDIT

Zhao Liang, Huawei Weiran Labs