Bug 691295

Summary: Segfault depends on length or number of LIBPATH paths
Product: Ghostscript Reporter: Dan Rosenberg <dan.j.rosenberg>
Component: PS InterpreterAssignee: Alex Cherepanov <alex>
Status: RESOLVED FIXED    
Severity: critical CC: christinedelight.top85, henry.stiles, jackie.rosen, jsmeix, till.kamppeter, werner, zappee
Priority: P4    
Version: 0.00   
Hardware: PC   
OS: Linux   
Customer: Word Size: ---
Attachments: Sample files
patch

Description Dan Rosenberg 2010-05-12 04:12:08 UTC
Please see the link below for a report of two exploitable memory corruption vulnerabilities in the Ghostscript interpreter, which are public as of May 11, 2010.

https://bugs.launchpad.net/ubuntu/+source/ghostscript/+bug/546009
Comment 1 Ken Sharp 2010-05-14 09:17:09 UTC
*** Bug 691303 has been marked as a duplicate of this bug. ***
Comment 2 Till Kamppeter 2010-05-14 09:31:21 UTC
Dan, if you report a bug to both Ubuntu and to upstream, please also add a link to the upstream bug in the Ubuntu bug report. Use the "Also affects project" link for that. This avoids a lot of duplicate work. Thanks.
Comment 3 Alex Cherepanov 2010-06-04 00:15:04 UTC
Created attachment 6349 [details]
Sample files

Local copy of the sample files and a comment from the original bug report:

I have discovered multiple memory corruption vulnerabilities in Ghostscript, which can be triggered when using Ghostscript to view maliciously crafted PostScript files.

1. The Ghostscript interpreter fails to properly handle some cases of infinite recursion. By creating a .ps file with a sequence such as:

/A{pop 0 A 0} bind def
/product A 0

The interpreter's internal stack will be overflowed with recursive calls. Rather than gracefully handling this situation, the interpreter continues execution by jumping to an (usually invalid) address near (or past) the tail end of the stack. Without further manipulation, this would simply result in a segfault, but it turns out that by altering the number of variable definitions that occur before the call to the infinitely recursive procedure, the user can actually exert control over the address that is jumped to. Combined with the fact that the attacker has an easy way to introduce shellcode (via the .ps file), this can definitely result in arbitrary code execution. I have not developed a fully functional exploit for this case, but the attached reproducer ("infinite.ps") will trigger a segfault in the same location on all of the versions of Ghostscript I have tested, including 8.61, 8.62, 8.64, and 8.70. If you wish to more convincingly verify that this is exploitable, place varying amounts of "/A{ 0 } bind def" strings at the beginning of the file, and observe how the EIP at crash time is altered. Unfortunately, the Ghostscript code is rather complex, so I am unable to determine the root cause of this vulnerability in the source.

2. Ghostscript fails to properly parse very long identifiers, leading to memory corruption and potentially arbitrary code execution. The resulting behavior depends on the version of Ghostscript. I have found that 8.61 (Hardy) is not vulnerable. However, 8.64 (Jaunty) is vulnerable and exploitable. I haven't been able to pin down the exact cause for the overflow, but the exploitable memory access occurs in gc_objects_clear_marks(), in psi/igc.c, when *(pre + 1) is called as a function. 8.70 (Karmic) is also vulnerable to an overflow when parsing very long identifiers, but this time, rather than an invalid (exploitable) function pointer call, the issue is a classic stack overflow. The overflow occurs due to a bad memcpy() call in dynamic_save(), called in scan_token() in psi/iscan.c at line 1061. Successful exploitation is mitigated by SSP - it catches the overflow and terminates - so on Karmic this should only result in denial of service.

The attached reproducer ("overflow.ps") will trigger overflows in both 8.64 and 8.70 due to the different reasons described above.
Comment 4 Alex Cherepanov 2010-06-04 02:02:17 UTC
Created attachment 6350 [details]
patch

The bug demonstrated by overflow.ps is fixed in the current
development version.

The bug demonstrated by infinite.ps was introduced by the rev. 7964.
This patch reverts rev. 7964 and uses a different approach: allocate all
structures and proceed with their initialization only if all allocations
were successful.

Unfortunately, the patch causes some unexpected differences
that should be reviewed before the patch can be committed.
Comment 5 Dr. Werner Fink 2010-06-08 15:07:51 UTC
(In reply to comment #4)
> Created an attachment (id=6350) [details]
> patch
> 
> The bug demonstrated by overflow.ps is fixed in the current
> development version.

For maintenance the fixing patch would be perfect.
Comment 6 Alex Cherepanov 2010-06-22 06:04:37 UTC
The patch from the comment #4 has been committed as a rev. 11414.
All differences turned out to be unrelated to the patch and
caused some indeterministic errors in rendering.
Comment 7 Till Kamppeter 2010-06-22 08:28:25 UTC
(In reply to comment #4)
> The bug demonstrated by overflow.ps is fixed in the current
> development version.

Can someone post a patch to fix this problem in Ghostscript 8.71, so that the problem can also get fixed in already released distributions? Thanks.
Comment 8 Marcos H. Woehrmann 2010-06-22 15:21:12 UTC
(In reply to comment #7)
> (In reply to comment #4)
> > The bug demonstrated by overflow.ps is fixed in the current
> > development version.
> 
> Can someone post a patch to fix this problem in Ghostscript 8.71, so that the
> problem can also get fixed in already released distributions? Thanks.

The attached patch works for 8.71
Comment 9 Alex Cherepanov 2010-06-22 16:18:25 UTC
The patch reverts rev. 7694, not rev. 7964 as stated above.
Comment 10 Till Kamppeter 2010-06-22 17:07:53 UTC
Marcos, I do not mean the patch of comment #4 but the fix which solves the problem of overflow.ps in the current development version. I would also like to have a patch for that one.
Comment 11 Carl 2010-06-23 07:06:59 UTC
When will there be a new release for Windows?
Comment 12 Ken Sharp 2010-06-23 07:27:09 UTC
(In reply to comment #11)
> When will there be a new release for Windows?

Ghostscript releases are made at approximately 6 month intervals, the last release was February 2010, the next target release date is August 2010.
Comment 13 Alex Cherepanov 2010-07-18 23:41:24 UTC
Function gs_alloc_ref_array() allocates 2 blocks of memory with
alloc_save_change_alloc() and gs_alloc_struct_array().
Both rev. 7694 and rev. 11414 try to keep data structures consistent
when one of the allocations fails.

Originally, alloc_save_change_alloc() had side effects and rev. 7694 called it
last, but this approach causes regression noted in the bug report.

Rev. 11414 takes side effects out of gs_alloc_ref_array(), restores the call
order and patches alloc_change_t only when both allocations succeed.
Comment 14 Henry Stiles 2010-09-02 22:50:30 UTC
Reopening as the fix 11414 introduced a problem with the cet.

debugobj/gs -dBATCH -dMaxBitmap=10000 -dNOPAUSE -sDEVICE=ppmraw -sOutputFile=foo.ppm -r300 -dJOBSERVER %rom%Resource/Init/gs_cet.ps /Users/henrys/tests_private/ps/ps3cet/11-21.PS -c quit
GPL Ghostscript SVN PRE-RELEASE 8.72 (2010-02-11)
Copyright (C) 2010 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Loading NimbusSanL-Bold font from %rom%Resource/Font/NimbusSanL-Bold... 3549320 1976957 2775696 1426606 1 done.
% _Pg checksums collected from PhotoPRINT SE 5.0v2 version 3017.102 
11-21 SYNTAX 
Loading NimbusRomNo9L-Regu font from %rom%Resource/Font/NimbusRomNo9L-Regu... 3590536 2133747 2816064 1451214 1 done.
11-21 SYNTAX = 0 Graphic 10 ms 
/11-21__Pg01 0 def %matching 0 
11-21 GSTATE 
Segmentation fault
Comment 15 Alex Cherepanov 2010-09-06 03:10:16 UTC
I've tried Windows and Mac x86, Linux 32 and 64-bit, Linux PPC, v.11414 and HEAD,
debug and release. Everything works just fine. I need detailed description of the
platform where this bug can be reproduced.
Comment 16 Alex Cherepanov 2010-09-07 03:53:46 UTC
The problem is reproduced on Mac OS and Linux 64bit.
My compilation environment was a little different from the standard one.
I had search path:
   %rom%Resource/Init/ : %rom%lib/ :
   /home/alexcher/gs_svn/gs/Resource/Init :
   /home/alexcher/gs_svn/gs/Resource
but default build has
   . : %rom%Resource/Init/ : %rom%lib/ :
   /usr/local/share/ghostscript/8.72/Resource/Init :
   /usr/local/share/ghostscript/8.72/lib :
   /usr/local/share/ghostscript/8.72/Resource/Font :
   /usr/local/share/ghostscript/fonts :
   /usr/local/share/fonts/default/ghostscript :
   /usr/local/share/fonts/default/Type1 :
   /usr/local/share/fonts/default/TrueType : /usr/lib/DPS/outline/base :
   /usr/openwin/lib/X11/fonts/Type1 : /usr/openwin/lib/X11/fonts/TrueType :
   /usr/share/cups/fonts

I can avoid the crash by adding more search paths with -I option.
Before the crash -Z? reports "Bad object ...". The offending object is
gx_path structure.
Comment 17 Ray Johnston 2010-09-07 16:34:48 UTC
Changing the comment to reflect the current status since the vulnerabilities
were fixed -- now we just need to get a fix that doesn't segfault.

We don't want people to get concerned that the vulnerability wasn't fixed.
Comment 18 Ray Johnston 2010-09-15 23:17:04 UTC
please post a call stack for the crash condition in case it helps suggest what
is going on.
Comment 19 Henry Stiles 2010-09-15 23:23:44 UTC
(In reply to comment #18)
> please post a call stack for the crash condition in case it helps suggest what
> is going on.

#0  0x000000010011b6a5 in igc_reloc_struct_ptr (obj=0x105016ac0, gcst=0x7fff5fbfd590) at ./psi/igc.c:1282
#1  0x00000001005699ea in basic_reloc_ptrs (vptr=0x1022398c8, size=64, pstype=0x10090b800, gcst=0x7fff5fbfd590) at ./base/gsmemory.c:346
#2  0x000000010011b443 in gc_do_reloc (cp=0x1031039f0, mem=0x10202ee68, pstate=0x7fff5fbfd590) at ./psi/igc.c:1222
#3  0x0000000100118e7d in gs_gc_reclaim (pspaces=0x10206c7e0, global=0) at ./psi/igc.c:438
#4  0x00000001001e413e in context_reclaim (pspaces=0x10206c7e0, global=0) at ./psi/zcontext.c:278
#5  0x00000001000ca856 in gs_vmreclaim (dmem=0x10206c7d8, global=0) at ./psi/ireclaim.c:153
#6  0x00000001000ca5bc in ireclaim (dmem=0x10206c7d8, space=-1) at ./psi/ireclaim.c:75
#7  0x00000001000c278c in interp_reclaim (pi_ctx_p=0x101913fd8, space=-1) at ./psi/interp.c:415
#8  0x00000001000c6fcc in interp (pi_ctx_p=0x101913fd8, pref=0x7fff5fbfe7b0, perror_object=0x7fff5fbfe9c0) at ./psi/interp.c:1678
#9  0x00000001000c29f6 in gs_call_interp (pi_ctx_p=0x101913fd8, pref=0x7fff5fbfe8f0, user_errors=1, pexit_code=0x7fff5fbfe9dc, perror_object=0x7fff5fbfe9c0) at ./psi/interp.c:484
#10 0x00000001000c2826 in gs_interpret (pi_ctx_p=0x101913fd8, pref=0x7fff5fbfe8f0, user_errors=1, pexit_code=0x7fff5fbfe9dc, perror_object=0x7fff5fbfe9c0) at ./psi/interp.c:442
#11 0x00000001000b522a in gs_main_interpret (minst=0x101913f40, pref=0x7fff5fbfe8f0, user_errors=1, pexit_code=0x7fff5fbfe9dc, perror_object=0x7fff5fbfe9c0) at ./psi/imain.c:240
#12 0x00000001000b5ee2 in gs_main_run_string_end (minst=0x101913f40, user_errors=1, pexit_code=0x7fff5fbfe9dc, perror_object=0x7fff5fbfe9c0) at ./psi/imain.c:556
#13 0x00000001000b5d9c in gs_main_run_string_with_length (minst=0x101913f40, str=0x1033000e0 "<2f55736572732f68656e7279732f74657374735f707269766174652f70732f7073336365742f31312d32312e5053>.runfile", length=102, user_errors=1, pexit_code=0x7fff5fbfe9dc, perror_object=0x7fff5fbfe9c0) at ./psi/imain.c:514
#14 0x00000001000b5d09 in gs_main_run_string (minst=0x101913f40, str=0x1033000e0 "<2f55736572732f68656e7279732f74657374735f707269766174652f70732f7073336365742f31312d32312e5053>.runfile", user_errors=1, pexit_code=0x7fff5fbfe9dc, perror_object=0x7fff5fbfe9c0) at ./psi/imain.c:496
#15 0x00000001000b93ba in run_string (minst=0x101913f40, str=0x1033000e0 "<2f55736572732f68656e7279732f74657374735f707269766174652f70732f7073336365742f31312d32312e5053>.runfile", options=3) at ./psi/imainarg.c:815
#16 0x00000001000b9314 in runarg (minst=0x101913f40, pre=0x100612440 "", arg=0x103300080 "/Users/henrys/tests_private/ps/ps3cet/11-21.PS", post=0x10060c4bd ".runfile", options=3) at ./psi/imainarg.c:805
#17 0x00000001000b8f57 in argproc (minst=0x101913f40, arg=0x7fff5fbff767 "/Users/henrys/tests_private/ps/ps3cet/11-21.PS") at ./psi/imainarg.c:738
#18 0x00000001000b73c2 in gs_main_init_with_args (minst=0x101913f40, argc=13, argv=0x7fff5fbff538) at ./psi/imainarg.c:215
#19 0x00000001000011bc in main (argc=13, argv=0x7fff5fbff538) at ./psi/gs.c:96
Comment 20 Ken Sharp 2010-09-16 08:40:30 UTC
It may or may not be relevant, but I've been investigating a 'similar' bug recently with pdfwrite. I was going to request some help anyway because I don't know much about the memory manager. My call stack is:

	gsdll32.dll!refs_compact(const gs_memory_s * mem=0x015848a8, obj_header_s * pre=0x026f42f8, obj_header_s * dpre=0x026e0058, unsigned int size=0x000000e0)  Line 741	C
 	gsdll32.dll!gc_objects_compact(chunk_s * cp=0x024e2240, gc_state_s * gcst=0x0006dee4)  Line 1325 + 0x18 bytes	C
 	gsdll32.dll!gs_gc_reclaim(vm_spaces_s * pspaces=0x0194ea88, int global=0x00000001)  Line 468 + 0x10 bytes	C
 	gsdll32.dll!context_reclaim(vm_spaces_s * pspaces=0x0194ea88, int global=0x00000001)  Line 278 + 0x10 bytes	C
 	gsdll32.dll!gs_vmreclaim(gs_dual_memory_s * dmem=0x0194ea84, int global=0x00000001)  Line 153 + 0x13 bytes	C
 	gsdll32.dll!ireclaim(gs_dual_memory_s * dmem=0x0194ea84, int space=0xffffffff)  Line 75 + 0xd bytes	C
 	gsdll32.dll!interp_reclaim(gs_context_state_s * * pi_ctx_p=0x015813b4, int space=0xffffffff)  Line 415 + 0x13 bytes	C
 	gsdll32.dll!interp(gs_context_state_s * * pi_ctx_p=0x015813b4, const ref_s * pref=0x0006f2a0, ref_s * perror_object=0x0006f390)  Line 1678 + 0xb bytes	C
 	gsdll32.dll!gs_call_interp(gs_context_state_s * * pi_ctx_p=0x015813b4, ref_s * pref=0x0006f328, int user_errors=0x00000001, int * pexit_code=0x0006f38c, ref_s * perror_object=0x0006f390)  Line 484 + 0x11 bytes	C
 	gsdll32.dll!gs_interpret(gs_context_state_s * * pi_ctx_p=0x015813b4, ref_s * pref=0x0006f328, int user_errors=0x00000001, int * pexit_code=0x0006f38c, ref_s * perror_object=0x0006f390)  Line 443 + 0x19 bytes	C
 	gsdll32.dll!gs_main_interpret(gs_main_instance_s * minst=0x01581360, ref_s * pref=0x0006f328, int user_errors=0x00000001, int * pexit_code=0x0006f38c, ref_s * perror_object=0x0006f390)  Line 241 + 0x1c bytes	C
 	gsdll32.dll!gs_main_run_string_end(gs_main_instance_s * minst=0x01581360, int user_errors=0x00000001, int * pexit_code=0x0006f38c, ref_s * perror_object=0x0006f390)  Line 557 + 0x19 bytes	C
 	gsdll32.dll!gs_main_run_string_with_length(gs_main_instance_s * minst=0x01581360, const char * str=0x01bce6e0, unsigned int length=0x00000030, int user_errors=0x00000001, int * pexit_code=0x0006f38c, ref_s * perror_object=0x0006f390)  Line 515 + 0x15 bytes	C
 	gsdll32.dll!gs_main_run_string(gs_main_instance_s * minst=0x01581360, const char * str=0x01bce6e0, int user_errors=0x00000001, int * pexit_code=0x0006f38c, ref_s * perror_object=0x0006f390)  Line 498 + 0x26 bytes	C
 	gsdll32.dll!run_string(gs_main_instance_s * minst=0x01581360, const char * str=0x01bce6e0, int options=0x00000003)  Line 815 + 0x1c bytes	C
 	gsdll32.dll!runarg(gs_main_instance_s * minst=0x01581360, const char * pre=0x11174909, const char * arg=0x01954480, const char * post=0x107a2e84, int options=0x00000003)  Line 805 + 0x11 bytes	C
 	gsdll32.dll!argproc(gs_main_instance_s * minst=0x01581360, const char * arg=0x014d222b)  Line 738 + 0x19 bytes	C
 	gsdll32.dll!gs_main_init_with_args(gs_main_instance_s * minst=0x01581360, int argc=0x00000007, char * * argv=0x014d3ed0)  Line 215 + 0x10 bytes	C
 	gsdll32.dll!gsapi_init_with_args(void * lib=0x015812a0, int argc=0x00000007, char * * argv=0x014d3ed0)  Line 173 + 0x1c bytes	C
 	gswin32c.exe!main(int argc=0x00000005, char * * argv=0x014d21b0)  Line 417 + 0x17 bytes	C
 	gswin32c.exe!__tmainCRTStartup()  Line 318 + 0x19 bytes	C
 	gswin32c.exe!mainCRTStartup()  Line 187	C
 	kernel32.dll!76a8d0e9() 	
 	[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]	
 	ntdll.dll!770619bb() 	
 	ntdll.dll!7706198e() 	


With a full debug unoptimised GS there is no error, with a non-debug, optimised version I get a GPF. With a debug optimised version I get the above call stack a memory error dumped on stderr:

GPL Ghostscript SVN PRE-RELEASE 9.01: .\psi\igcref.c(741): Reloc error
 for refs 0x26e0058: reloc = 82648, stored = 17104

and then an abort and exit. There must be something about the file which exercises PostScript/pdfwrite in an unusual way, but I really have no idea what it is. Its also a 25 page file which doesn't fail until page 18 :-(
Comment 21 Carl 2010-09-29 07:41:49 UTC
I've seen that 9.00 is released, is the fix in that one?
Comment 22 Alex Cherepanov 2011-11-01 05:36:34 UTC
The problem no longer appears in current development code.

I can easily reproduce the problem in v.9.04 using the command line from
the comment #14.