Bug 697596

Summary: Use-After-Free in i_free_object()
Product: Ghostscript Reporter: Kamil Frankowicz <kamil.frankowicz>
Component: GeneralAssignee: Default assignee <ghostpdl-bugs>
Status: RESOLVED FIXED    
Severity: normal    
Priority: P4    
Version: master   
Hardware: PC   
OS: Linux   
Customer: Word Size: ---
Attachments: POC to trigger use-after-free (gs)

Description Kamil Frankowicz 2017-02-20 00:58:35 UTC
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
Comment 1 Ken Sharp 2017-02-20 01:23:37 UTC
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.
Comment 2 Ken Sharp 2017-02-20 01:50:04 UTC
Fixed in commit ecceafe3abba2714ef9b432035fe0739d9b1a283

Thanks for the report, very well spotted!