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