Bug 689927 - rangecheck in c_pdf14trans_write() causing 'unregistered' error
Summary: rangecheck in c_pdf14trans_write() causing 'unregistered' error
Status: NOTIFIED FIXED
Alias: None
Product: Ghostscript
Classification: Unclassified
Component: Graphics Library (show other bugs)
Version: master
Hardware: All All
: P1 normal
Assignee: Igor Melichev
URL: casper.ghostscript.com:/home/support/...
Keywords:
Depends on:
Blocks:
 
Reported: 2008-06-30 00:13 UTC by Ray Johnston
Modified: 2008-12-19 08:31 UTC (History)
0 users

See Also:
Customer: 850, 531
Word Size: ---


Attachments
fix_12983.pat (1.35 KB, patch)
2008-06-30 00:22 UTC, Ray Johnston
Details | Diff
patch.txt (2.84 KB, patch)
2008-06-30 03:37 UTC, leonardo
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ray Johnston 2008-06-30 00:13:20 UTC
A check for the amount of data being written for a transparency mask aginst
"MAX_CLIST_COMPOSITOR_SIZE" in c_pdf14trans_write() was failing because this
#definewas calculated incorrectly.

I suspect that we had never before seen a combination of a small
GS_CLIENT_COLOR_MAX_COMPONENTS value (fails at 8 or less) that is recommended to
for performance improvement with a transparency mask that included Background
color and a non-identity transfer function for the soft mask.

The 'size' in this case was 320 bytes and the MAX_CLIST_COMPOSITOR calculation
was 317 -- close but no cigar. A larger number of background color components
would cause a failure even with a (slightly) larger GS_CLIENT_COLOR_MAX_COMPONENTS.

The command line that caused the error was:

gswin32c -r300 -sDEVICE=tiff32nc -o x.tif 12983.pdf

(with GS_CLIENT_COLOR_MAX_COMPONENTS == 8 or less in the build)

My calculations show that the +1 in the #define needs to be +46. A patch will
be attached for review. I STRONGLY suggest commenting both areas to (maybe)
prevent this from recurring.
Comment 1 Ray Johnston 2008-06-30 00:20:19 UTC
The test file location is given in the "URL" field, and must be downloaded
with ssh (account on casper required). It is 53 Mb.

This affects a P1 customer.
Comment 2 Ray Johnston 2008-06-30 00:22:05 UTC
Created attachment 4169 [details]
fix_12983.pat

This should be reviewed by Igor, but I STRONGLY suggest adding cautionary
comments
in to help make this less likely in the future.
Comment 3 leonardo 2008-06-30 03:37:58 UTC
Created attachment 4170 [details]
patch.txt

The patch 4169 has portability problems, attaching an improved one. I can't
test the 53M file before Thirsday because I'm on an extremily slow and
expensive GPRS connection. I'll add a result of a local testing with
comparefiles when it is ready.
Comment 4 leonardo 2008-06-30 07:15:05 UTC
Local testring didn't detect any problem.
Comment 5 Ray Johnston 2008-06-30 08:48:03 UTC
Yes, this portability is better (my patch was "quick and dirty" and assumed
4-byte floats). Also I had missed that the CTM was actually variable length up
to 25 bytes, not the 10 bytes I had observed in the problem.

Also, I like the improvement that uses this #define constant to allocate the
stack based 'buf' since clearly we previously had a buffer overflow potential
(by at least the size of the bbox).

It would be nicer to check the size BEFORE writing the buffer, however. The
current code only checks and returns an error after the damage is done.

One question:

Why is the 'mask_id' included twice in the MAX_CLIST_COMPOSITOR_SIZE calculation?

! 	     sizeof(((gs_pdf14trans_params_t *)0)->mask_id) + \
! 	     sizeof(((gs_pdf14trans_params_t *)0)->Background) + \
! 	     sizeof(((gs_pdf14trans_params_t *)0)->GrayBackground) + \
! 	     sizeof(((gs_pdf14trans_params_t *)0)->transfer_fn) + \
! 	     sizeof(((gs_pdf14trans_params_t *)0)->mask_id) )

I will test the patch against the 53Mb file (after removing the duplicate
mask_id).
Comment 6 Ray Johnston 2008-06-30 09:11:59 UTC
The 53Mb file runs fine with the patch (modified to remove the duplicate
mask_id in the size calculation).

Note I still recommend adding a comment to the code, but perhaps since the
'buf' now refers to the #define constant, the linkage is a bit more obvious.

A change to detect potential overflow BEFORE writing the buffer is warranted,
IMHO, otherwise, why have a check at all? After all, buffer overflows of stack
based buffers are frequent exploits for security breaches.
Comment 7 leonardo 2008-06-30 10:29:51 UTC
Marcos,

You wrote : "It would be nicer to check the size BEFORE writing the buffer". 
Hmm, it may be done with some intrivial change: (1) create a stream with 
swrite_position_only, (2) write all data to there, (3) check the actuall 
length, (4) create another stream with swrite_string, (5) write to it. It will 
require to convert the serialization code into one with a stream output. I 
don't think it worth the trouble : the simpler post-factum check worked fine 
(since the bug is open), and there are many other important things to do. We 
already have got a similar logic in cmd_put_drawing_color, but its maintanance 
appears too hard. Actually what I coded is a simplified *overestimation* of 
the required length.

You wrote : "Note I still recommend adding a comment to the code". I added : 
buf /* Must be large enough to fit the data written below. */ and 
/*
   The longest compositor command includes transparency mask parameters
   (see gdevp14.c c_pdf14trans_write() the "case PDF14_BEGIN_TRANS_MASK"). */
Isn't it sufficient ?

You wrote : "Why is the 'mask_id' included twice". Oops, the second one has to 
be removed.

Marcos, do you want to comit the patch and close the bug, or you want me to do 
that ? I think it is mainly your product.    				
		      
Comment 8 Ray Johnston 2008-06-30 10:54:05 UTC
Igor,

The comments (and the original patch) were from me (Ray) not Marcos.

Please commit the patch (you are the owner of the clist logic), unless you
feel that this is a transparency issue in which case I will consult with Michael
(he is the owner of the color and transparency code).

Since you reviewed and improved this patch, I think you should commit it.

On the topic of detecting overflow BEFORE writing, I think it is safe to assume
that there will always be enough space for the "fixed length" elements, and
simply do checking before writing the larger chunks (Background Color * number
of BG components) and transfer function. The check can be as simple as
   if ((pbuf+l+sizeof(GrayBackground)-buf) >
        MAX_CLIST_TRANSPARENCY_COMPOSITOR_SIZE)

Also, it appears from another examination of the code, that the
'transfer_fucntion' is never written to the 'buf' array, but rather is copied
directly from pparams->transfer_function, so the:

   sizeof(((gs_pdf14trans_params_t *)0)->transfer_fn) 

does not need to be included in the buf size, but it _IS_ part of the entire
compositor size. So it seems that you could reduce the buffer allocation to
       byte buf[MAX_CLIST_TRANSPARENCY_COMPOSITOR_SIZE -
                sizeof(((gs_pdf14trans_params_t *)0)->transfer_fn)];

I'm rather just as happy to have the wasted 256 bytes on the stack as extra
guarantee against buffer overflow.

Please use your own judgement.
Comment 9 leonardo 2008-06-30 14:09:59 UTC
Regarding to Comment #8 : 

Well I'm happy that collegues pay so big attention to this simple patch. 
Spending a lot of man*hours we will come to a really optimal solution. 
Ray's "simple" check doesn't account the number of components of Background 
(not sure why), so it neeads further improvements. transfer_fn is not written 
to the buffer but must be accounted in MAX_CLIST_TRANSPARENCY_COMPOSITOR_SIZE, 
which is used elsewhere. Well we can save 256 butes from the single element of 
the stack with a further complexication of #defines.

Nevertheless the great problem here is the number of assumptions done when 
coding the check. Ray suggests to add one more : there exist fixed size 
elements, which may be estimated in advance. One can prove it with reading 
put_value, etc. Well it has to be documented. But IMO going in this way we 
fall into a risk of making logical errors while combining the assumptions. 
Thus the save solution is as explained in Comment 7, but it is too expewnsive 
in both man*hours and in CPU time, everything else is partially safe and 
partially risky. I'm not sure that we're doing a right thing spending so many 
man*hours for seeking for the optimal compromise between two vague issues, 
which both are not well defined. 

Due to all that I suggesteg to continue this effort to those who likes it. 
Otherwise I'll commit it tomorrow. My local time is too late, and it gives a 
gap for others.
Comment 10 leonardo 2008-07-01 02:05:46 UTC
Now I slept well and reviewed it with a fresh head. Neither old nor new code 
implements a check for the local buffer overflow. The check is about 
overflowing the clist interpreter buffer, which is unrelated to the local 
buffer. The stack damage appears with the old code because the local buffer 
was too small : it's length was not updated when the writing code was enhanced 
with bbox in revision 8587. 

So now I discontinue the effort to implement a dynamic check for the local 
buffer overflow, and provide better comments. The patch is being tested 
locally (except for 58M file) and will be committed in few hours.

Also I saved 256K of stack from transfer_fn. Nevertheless the expense for 
Background still persists. This field consumes 8 times bigger. 

If Support wants further improvements, a separate bug (or enhancement ?) to be 
opened and assigned to the module owner. The module was initially written by 
Raph and later it was fragmentary rewritten by Leo, so I believe one can find 
many other inaccurate things in it. As to me. I'll return to the P1 bug I'm 
working now.
Comment 11 leonardo 2008-07-01 03:25:05 UTC
Patch to HEAD :
http://ghostscript.com/pipermail/gs-cvs/2008-July/008396.html