Heap buffer overflow in AcquireCacheNexus()
Swiss army knife of image processing
Brought to you by:
bfriesen
After some fuzz testing I found a crashing test case.
Changeset: 15133:198ea602ea7c
Command: gm convert -negate -clip gm_hbo_AcquireCacheNexus /dev/null
ASAN Context:
==1579==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6030000000bf at pc 0x000000653554 bp 0x7ffe750132f0 sp 0x7ffe750132e8
WRITE of size 4 at 0x6030000000bf thread T0
#0 0x653553 in AcquireCacheNexus XYZ/GM/magick/pixel_cache.c:919:21
#1 0x653867 in AcquireCacheViewPixels XYZ/GM/magick/pixel_cache.c:995:10
#2 0x653867 in AcquireImagePixels XYZ/GM/magick/pixel_cache.c:1091
#3 0x6d463d in IntegralRotateImage XYZ/GM/magick/shear.c:892:29
#4 0x6d11ac in RotateImage XYZ/GM/magick/shear.c:1669:18
#5 0x902306 in ReadWPGImage XYZ/GM/coders/wpg.c:1159:39
#6 0x595019 in ReadImage XYZ/GM/magick/constitute.c:1607:13
#7 0x51a1c0 in ConvertImageCommand XYZ/GM/magick/command.c:4348:22
#8 0x52fcf1 in MagickCommand XYZ/GM/magick/command.c:8869:17
#9 0x567933 in GMCommandSingle XYZ/GM/magick/command.c:17396:10
#10 0x566633 in GMCommand XYZ/GM/magick/command.c:17449:16
#11 0x7f79981ae82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#12 0x41beb8 in _start (/usr/local/bin/gm+0x41beb8)
0x6030000000bf is located 1 bytes to the right of 30-byte region [0x6030000000a0,0x6030000000be)
allocated by thread T0 here:
#0 0x4c2bbc in __interceptor_malloc /scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:66:3
#1 0x61e7d2 in MagickRealloc XYZ/GM/magick/memory.c:471:18
#2 0x65a9c9 in ModifyCache XYZ/GM/magick/pixel_cache.c:2955:18
#3 0x65e965 in SetCacheNexus XYZ/GM/magick/pixel_cache.c:3891:7
#4 0x65f107 in SetCacheViewPixels XYZ/GM/magick/pixel_cache.c:3970:10
#5 0x65f107 in SetImagePixelsEx XYZ/GM/magick/pixel_cache.c:4082
SUMMARY: AddressSanitizer: heap-buffer-overflow XYZ/GM/magick/pixel_cache.c:919:21 in AcquireCacheNexus
Shadow bytes around the buggy address:
0x0c067fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff8000: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
=>0x0c067fff8010: 05 fa fa fa 00 00 00[06]fa fa fd fd fd fa fa fa
0x0c067fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1579==ABORTING
I am able to reproduce this.
Hmm, I can also reproduce this, but it seems that
MagickExport void ReplaceImageInList(Image *images,Image image)
does some unexpected think. First call passes OK, second call crashes.
Just simply because a crash point seems to be located on another place, it seems to be some memory corruption somewhere else.
> gm.exe!_free_dbg_nolock(void * pUserData=0x00fc5fa8, int nBlockUse=1) Line 1333 + 0x3b bytes C++ gm.exe!_free_dbg(void * pUserData=0x00fc5fa8, int nBlockUse=1) Line 1220 + 0xd bytes C++ gm.exe!free(void * pUserData=0x00fc5fa8) Line 1178 + 0xb bytes C++ gm.exe!MagickFree(void * memory=0x00fc5fa8) Line 509 + 0xc bytes C gm.exe!DestroyCacheInfo(_CacheInfo * cache_info=0x00fbd6c0) Line 1856 + 0x12 bytes C gm.exe!DestroyImagePixels(_Image * image=0x00fbbc50) Line 1952 + 0xf bytes C gm.exe!DestroyImage(_Image * image=0x00fbbc50) Line 1261 + 0x9 bytes C gm.exe!ReplaceImageInList(_Image * * images=0x0013d4b0, _Image * image=0x00fc7b38) Line 778 + 0xb bytes C gm.exe!ReadWPGImage(const _ImageInfo * image_info=0x00fb2180, _ExceptionInfo * exception=0x0013f6ec) Line 1174 + 0xd bytes C gm.exe!ReadImage(const _ImageInfo * image_info=0x00fb0068, _ExceptionInfo * exception=0x0013f6ec) Line 1607 + 0x18 bytes C gm.exe!ConvertImageCommand(_ImageInfo * image_info=0x00fb0068, int argc=3, char * * argv=0x003ded70, char * * metadata=0x00000000, _ExceptionInfo * exception=0x0013f6ec) Line 4348 + 0xd bytes C gm.exe!MagickCommand(_ImageInfo * image_info=0x00fb0068, int argc=3, char * * argv=0x003d3af4, char * * metadata=0x0013f714, _ExceptionInfo * exception=0x0013f6ec) Line 8870 + 0x34 bytes C gm.exe!GMCommandSingle(int argc=3, char * * argv=0x003d3af4) Line 17396 + 0x22 bytes C gm.exe!GMCommand(int argc=4, char * * argv=0x003d3af0) Line 17449 + 0xd bytes C gm.exe!main(int argc=4, char * * argv=0x003d3af0) Line 61 + 0xd bytes C gm.exe!__tmainCRTStartup() Line 327 + 0x19 bytes C gm.exe!mainCRTStartup() Line 196 CLast edit: Jaroslav Fojtik 2017-09-10
Mercurial changesets 15161:3dc7b4e3779d and 15245:e8086faa52d0 (and possibly 15246:2b7c826d36af) seem to help in that ASAN no longer reports a heap buffer overflow. Valgrind still reports use of uninitialized data and ASAN complains bout memory leaks.
After the addition of Mercurial changesets 15247:75245a215fff, 15248:fcd3ed3394f6, 15249:135bdcb88b8d, 15250:2a21cda3145b, and 15251:1b9e64a8901e, this problem is definitely fixed and additional issues noticed by valgrind and ASAN are fixed as well.