Created attachment 13410 [details] POC to trigger use-after-free (gs) After some fuzz testing I found a crashing test case. HEAD: 4d2b25f06c25e6e3e2b1bf319481a7442d42af8a Command: gs -dNOPAUSE -sDEVICE=bit -sOUTPUTFILE=/dev/null -dSAFER gs_uaf_i_free_object -c quit ASAN: ==20050==ERROR: AddressSanitizer: heap-use-after-free on address 0x62b000000258 at pc 0x0000013f34f7 bp 0x7ffd6a96d7b0 sp 0x7ffd6a96d7a8 READ of size 8 at 0x62b000000258 thread T0 #0 0x13f34f6 in i_free_object XYZ/ghostpdl/./base/gsalloc.c:1418:18 #1 0x17fed3f in gx_begin_image1 XYZ/ghostpdl/./base/gximage1.c:99:9 #2 0x18a7e16 in gx_default_begin_typed_image XYZ/ghostpdl/./base/gdevddrw.c:1059:12 #3 0x18a79f8 in gx_default_begin_image XYZ/ghostpdl/./base/gdevddrw.c:1024:12 #4 0x18a7d93 in gx_default_begin_typed_image XYZ/ghostpdl/./base/gdevddrw.c:1051:24 #5 0x1476174 in gs_image_begin_typed XYZ/ghostpdl/./base/gsimage.c:252:12 #6 0x1b344e5 in zimage_setup XYZ/ghostpdl/./psi/zimage.c:184:9 #7 0x1b35cff in image1_setup XYZ/ghostpdl/./psi/zimage.c:246:12 #8 0x1a22d37 in interp XYZ/ghostpdl/./psi/interp.c:1578:40 #9 0x1a22d37 in gs_call_interp XYZ/ghostpdl/./psi/interp.c:511 #10 0x1a22d37 in gs_interpret XYZ/ghostpdl/./psi/interp.c:468 #11 0x19f5ab2 in gs_main_interpret XYZ/ghostpdl/./psi/imain.c:238:12 #12 0x19f5ab2 in gs_main_run_string_end XYZ/ghostpdl/./psi/imain.c:656 #13 0x19f5ab2 in gs_main_run_string_with_length XYZ/ghostpdl/./psi/imain.c:614 #14 0x1a019ee in run_string XYZ/ghostpdl/./psi/imainarg.c:977:16 #15 0x1a019ee in runarg XYZ/ghostpdl/./psi/imainarg.c:967 #16 0x1a00e48 in argproc XYZ/ghostpdl/./psi/imainarg.c:900:16 #17 0x19f9963 in gs_main_init_with_args XYZ/ghostpdl/./psi/imainarg.c:238:24 #18 0x5475d8 in main XYZ/ghostpdl/./psi/gs.c:96:16 #19 0x7f9f5c0d582f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #20 0x47ba08 in _start (/usr/local/bin/gs+0x47ba08) 0x62b000000258 is located 88 bytes inside of 24928-byte region [0x62b000000200,0x62b000006360) freed by thread T0 here: #0 0x519ecb in __interceptor_free /home/llvm/clang-3.9/final/llvm.src/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:47:3 #1 0x148af3f in gs_heap_free_object XYZ/ghostpdl/./base/gsmalloc.c:354:5 previously allocated by thread T0 here: #0 0x51a21c in malloc /home/llvm/clang-3.9/final/llvm.src/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64:3 #1 0x148a4ff in gs_heap_alloc_bytes XYZ/ghostpdl/./base/gsmalloc.c:189:34 SUMMARY: AddressSanitizer: heap-use-after-free XYZ/ghostpdl/./base/gsalloc.c:1418:18 in i_free_object Shadow bytes around the buggy address: 0x0c567fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c567fff8000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c567fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c567fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c567fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c567fff8040: fd fd fd fd fd fd fd fd fd fd fd[fd]fd fd fd fd 0x0c567fff8050: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c567fff8060: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c567fff8070: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c567fff8080: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c567fff8090: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 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 Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 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 ==20050==ABORTING
There is confusion over ownership of 'penum' between gx_begin_image1(), gx_begin_image4() and gx_image_enum_begin() which is called form these two functions. The enumerator is allocated in gx_begin_image?() and freed there if gx_image_enum_begin() returns an error, which is I believe the correct course. However, gx_image_enum_begin() also frees the enumerator on an error, except that it doesn't always do so. Its a large function and there are at least 9 ways to exit it, only 4 of which free the enumerator. Since gx_image_enum_begin() didn't allocate the memory I feel it should not free it either. My proposal for this is to remove the cases in here where the enumerator memory is freed.
Fixed in commit ecceafe3abba2714ef9b432035fe0739d9b1a283 Thanks for the report, very well spotted!