An invalid write may occur in optipng 0.7.5 while processing bitmap
images due to crt_row being (inc|dec)remented without any boundary
checking when encountering delta escapes.
optipng-0.7.5/src/pngxtern/pngxrbmp.c:
210 static size_t 211 bmp_read_rows(png_bytepp begin_row, png_bytepp end_row, size_t row_size, 212 unsigned int compression, FILE *stream) 213 { ... 272 crt_row = begin_row; 273 for ( ; ; ) 274 { 275 ch = getc(stream); b1 = (unsigned int)ch; 276 ch = getc(stream); b2 = (unsigned int)ch; 277 if (ch == EOF) 278 break; 279 if (b1 == 0) /* escape */ 280 { ... 307 else if (b2 == 2) /* delta */ 308 { 309 ch = getc(stream); b1 = (unsigned int)ch; /* horiz. offset */ 310 ch = getc(stream); b2 = (unsigned int)ch; /* vert. offset */ ... 314 if (b2 > (size_t)((end_row - crt_row) * inc)) 315 b2 = (unsigned int)((end_row - crt_row) * inc); 316 for ( ; b2 > 0; --b2) 317 { ... 319 crt_row += inc; ... 322 } ... 324 } 325 else /* b2 >= 3 bytes in absolute mode */ 326 { 327 len = (b2 <= endn - crtn) ? b2 : (unsigned int)(endn - crtn); 328 if (bmp_fread_fn(*crt_row, crtn, len, stream) != len) 329 break; 330 crtn += len; 331 } 332 } ... 352 }
A delta escape moving crt_row beyond its allocated chunk followed by
fread() in absolute mode would, with GNU libc combined with a !topdown
image, result in a write to an address based on the specified bitmap
height:
$ ./crash oob.bmp $ gdb --args optipng oob.bmp (gdb) r ** Processing: oob.bmp Program received signal SIGSEGV, Segmentation fault. __memcpy_sse2 () at ../sysdeps/x86_64/multiarch/../memcpy.S:96 96 ../sysdeps/x86_64/multiarch/../memcpy.S: No such file or directory. (gdb) bt #0 __memcpy_sse2 () at ../sysdeps/x86_64/multiarch/../memcpy.S:96 #1 0x00007ffff7a89003 in __GI__IO_file_xsgetn (fp=0x64a010, data=<optimized out>, n=4) at fileops.c:1371 #2 0x00007ffff7a7e5f0 in __GI__IO_fread (buf=<optimized out>, size=1, count=4, fp=0x64a010) at iofread.c:42 #3 0x000000000040b632 in bmp_rle4_fread (ptr=0x4141 <error: Cannot access memory at address 0x4141>, offset=0, len=8, stream=0x64a010) at pngxrbmp.c:170 #4 0x000000000040baf6 in bmp_read_rows (begin_row=0x64e668, end_row=0x64a538, row_size=4, compression=2, stream=0x64a010) at pngxrbmp.c:328 #5 0x000000000040cb0e in pngx_read_bmp (png_ptr=0x64a240, info_ptr=0x64a4b0, stream=0x64a010) at pngxrbmp.c:724 #6 0x000000000040b352 in pngx_read_image (png_ptr=0x64a240, info_ptr=0x64a4b0, fmt_name_ptr=0x7fffffffbf10, fmt_long_name_ptr=0x0) at pngxread.c:130 #7 0x00000000004043cc in opng_read_file (infile=0x64a010) at optim.c:939 #8 0x000000000040586a in opng_optimize_impl (infile_name=0x7fffffffe86f "oob.bmp") at optim.c:1503 #9 0x0000000000406749 in opng_optimize (infile_name=0x7fffffffe86f "oob.bmp") at optim.c:1853 #10 0x0000000000402bf0 in process_files (argc=2, argv=0x7fffffffe638) at optipng.c:941 #11 0x0000000000402cb5 in main (argc=2, argv=0x7fffffffe638) at optipng.c:975 (gdb) x/i $rip => 0x7ffff7aa3427 <__memcpy_sse2+55 at ../sysdeps/x86_64/multiarch/../memcpy.S:96>: mov %ecx,(%rdi) (gdb) p/x $ecx $1 = 0x11223344 (gdb) p/x $rdi $2 = 0x4141 (gdb)
Thank you very much for the very elaborate report. I am marking this as security-sensitive. There are other known BMP issues that need fixing, and I am planning to make a new release soon.
This issue has been assigned CVE-2016-2191. Is there an ETA for the new release?
Last edit: Hans Jerry Illikainen 2016-03-15
Yes: hopefully, sometime this week.
Fixed in the new version 0.7.6.