You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by bu...@apache.org on 2005/05/05 20:52:26 UTC

DO NOT REPLY [Bug 34769] New: - ArrayIndexOutOfBoundsException occurs when reading more than 8192 bytes from the BufferedReader supplied by HttpServletRequest.getReader()

DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG�
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://issues.apache.org/bugzilla/show_bug.cgi?id=34769>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND�
INSERTED IN THE BUG DATABASE.

http://issues.apache.org/bugzilla/show_bug.cgi?id=34769

           Summary: ArrayIndexOutOfBoundsException occurs when reading more
                    than 8192 bytes from the BufferedReader supplied by
                    HttpServletRequest.getReader()
           Product: Tomcat 5
           Version: 5.5.4
          Platform: PC
        OS/Version: Windows XP
            Status: NEW
          Severity: normal
          Priority: P3
         Component: Catalina:Modules
        AssignedTo: tomcat-dev@jakarta.apache.org
        ReportedBy: gdavis@spicer.com


Well...I can supply quite a bit of info, but I can't say you'll have an easy 
time reproducing the problem.

First off, let me provide you with the exception that we experienced.

java.lang.ArrayIndexOutOfBoundsException
        at java.lang.System.arraycopy(Native Method)
        at org.apache.tomcat.util.buf.CharChunk.append(CharChunk.java:298)
        at org.apache.tomcat.util.buf.B2CConverter.convert
(B2CConverter.java:97)
        at org.apache.tomcat.util.buf.B2CConverter.convert
(B2CConverter.java:76)
        at org.apache.catalina.connector.InputBuffer.realReadChars
(InputBuffer.java:339)
        at org.apache.tomcat.util.buf.CharChunk.substract(CharChunk.java:384)
        at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:350)
        at org.apache.catalina.connector.CoyoteReader.read
(CoyoteReader.java:80)
        at com.spicer.jdl.MatrixXMLSerializable.decodeMime64
(MatrixXMLSerializable.java:142)
        at com.spicer.jdl.MatrixXMLSerializable.parseMime64
(MatrixXMLSerializable.java:523)
        at com.spicer.jdl.MatrixXMLSerializable.decodeByteArrayRaw
(MatrixXMLSerializable.java:710)
        at com.spicer.jdl.MatrixXMLSerializable.decodeByteArray
(MatrixXMLSerializable.java:641)
        at com.spicer.jdl.MatrixXMLDispatch.dispatch_ClientWrapperServer
(MatrixXMLDispatch.java:109)
        at com.spicer.jdl.MatrixXMLDispatch.dispatch(MatrixXMLDispatch.java:62)
        at com.spicer.servlet.MatrixXMLServlet.doPost(Unknown Source)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter
(ApplicationFilterChain.java:237)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter
(ApplicationFilterChain.java:157)
        at org.apache.catalina.core.StandardWrapperValve.invoke
(StandardWrapperValve.java:214)
        at org.apache.catalina.core.StandardContextValve.invoke
(StandardContextValve.java:178)
        at org.apache.catalina.core.StandardHostValve.invoke
(StandardHostValve.java:126)
        at org.apache.catalina.valves.ErrorReportValve.invoke
(ErrorReportValve.java:105)
        at org.apache.catalina.core.StandardEngineValve.invoke
(StandardEngineValve.java:107)
        at org.apache.catalina.connector.CoyoteAdapter.service
(CoyoteAdapter.java:148)
        at org.apache.coyote.http11.Http11Processor.process
(Http11Processor.java:825)
        at 
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnecti
on(Http11Protocol.java:731)
        at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket
(PoolTcpEndpoint.java:526)
        at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt
(LeaderFollowerWorkerThread.java:80)
        at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run
(ThreadPool.java:684)
        at java.lang.Thread.run(Thread.java:595)


This exception occurred in Tomcat 5.5.4 whenever we had a larger post to the 
servlet.

We have a java based client-server product that uses a servlet when being 
deployed for internet use.  Essentially, the client converts the calls into an 
XML stream which it posts to the servlet (called MatrixXMLServlet) that then 
converts the data back into an RMI call for the server portion of the product.

This servlet has been in use, unchanged, for nearly 4 years and hasn't caused 
an error until now.  It works fine with versions of Tomcat before 5.0, but 
doesn't with 5.5 (I'm not 100% sure about 5.0 so I'll leave it out).

Using the FAQ as a guide, I searched for solutions and/or others with the same 
problem, but was unable to find anything.  So, I traced through the Tomcat 
source code to see if I could understand the problem...and I'm convinced (with 
little doubt) that there is a Tomcat bug...but I'm not sure what causes the 
classes to be set up this way.  So...I'll describe what I found...maybe it 
will help.

First of all, the doPost() in MatrixXMLServlet does a getReader() on the 
HttpServletRequest it is passed, and this reader is then set in the 
MatrixXMLSerializable object.  Then, in the decodeMime64() method, we loop 
reading one character at a time from the BufferedReader (as supplied by 
getReader()) and process it until we exhaust the stream.  But, on larger posts 
(I think those greater than 8192 characters), we get the exception listed 
above when trying to read that single character.

Now, in this case, the BufferedReader supplied is a CoyoteReader which in turn 
has an InputBuffer, which in turn has a CharChunk, a ByteChunk, and a 
B2CConverter.  For a read, the CoyoteReader does a read on InputBuffer, which 
then asks CharChunk for the character.  The CharChunk returns the next 
character that it has in its buffer, but if it has reached the end of what is 
available it asks InputBuffer for x more characters (where x is the amount of 
space left in its buffer).

Now...here is the problem number 1 ...rather than providing the CharChunk with 
at most x characters (or resetting the CharChunk if x=0), the InputBuffer asks 
B2CConverter to transfer everything that ByteChunk has in its buffer (and then 
refills ByteChunk).

So...if there is not enough space left in the CharChunk buffer to add this 
much data, it tries to accomodate by filling to the limit with what will fit, 
flushing the buffer and then adding the remainder.  But, this leads to the 
problem we see, because of what the flush actually does (or more 
precisely "doesn't do").  Essentially, in our case, the start and end pointers 
are at the same place (which is why CharChunk asks for more data in the first 
place) and so after filling to the end of the buffer, the flush actually does 
nothing (except set a flag in the InputBuffer) and then trying to add the rest 
of the data into the buffer causes it to go past the end of the buffer and 
generates our exception.  That is problem number 2.

BUT...if this 2nd insert into the CharChunk buffer doesn't cause the exception 
we have, then you've lost the data from the 1st insert, because it has been 
overwritten by the 2nd insert...thus losing data out of the input stream.  
Okay...this is problem number 3.

One possible solution would be to allow the buffer in CharChunk to "wrap" 
around the end...but then you have to be careful that you don't add more to 
the start of the buffer than can fit, or you have another case of losing data 
from the overlap of the 1st insert and the 2nd insert.

And...you can't simply reset the start pointer (in CharChunk) upon a flush, 
because then you'd loose all the data from the 1st insert again...and that's a 
no-no.

I think the whole thing needs to be examined a little more carefully.
I suspect that solving problem 1 (and also providing a way to "reset" the 
CharChunk...perhaps when it requests x=0 more characters) would make the whole 
thing work as desired.


Anyway...we have changed our code in MatrixXMLServlet so that instead of using 
getReader(), we use getInputStream()...and using 
HttpServletRequest.getCharacterEncoding(), we wrap the input stream inside an 
InputStreamReader, and then wrap that inside a BufferedReader.
Thus, using this alternative way to read the inputstream eliminates the 
problem for us.


Now...to post this bug, I tried to reproduce the problem with a small, and 
simple, servlet that simply used the getReader() call and then consumed the 
input stream one character at a time...and then generating sufficiently large 
posts to exceed the 8K size.  But, the problem didn't occur.

So...I don't know what must happen to exactly cause the setup we see inside 
the CoyoteReader.  We have a working solution now, and I can't afford anymore 
time tracing and debugging Tomcat code...so I hope this is of some help to you.

If you have questions, feel free to ask and I'll answer to the best of my 
knowledge.

Thanks.
Greg

-- 
Configure bugmail: http://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org