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
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