https://issues.apache.org/bugzilla/show_bug.cgi?id=53119

             Bug #: 53119
           Summary: java.nio.BufferOverflowException in
                    AjpAprProcessor.output() when AJP client disconnects
           Product: Tomcat 7
           Version: 7.0.26
          Platform: PC
            Status: NEW
          Severity: minor
          Priority: P2
         Component: Connectors
        AssignedTo: dev@tomcat.apache.org
        ReportedBy: prei...@web.de
    Classification: Unclassified


As reported on the users list [2] (note that I used Tomcat 7.0.27, but Bugzilla
seems to have only 7.0.26):

Hi all,

some time ago, I wrote about a BufferOverflowException which I got sporadically
on my Tomcat 7.0.21 [1], when using the AJP-APR-Connector.
However, at that time I couldn't reproduce the exception.

When I looked at this again today, I was able to create a client that sends an
AJP message and aborts the connection, resulting in the BufferOverflowException
in Tomcat. I was using Tomcat 7.0.27 with Tomcat Native 1.1.23, on Oracle JDK
1.7.0_03 and Windows 7 32-bit.


To reproduce,

1. On the server side, create a web application containing a servlet which will
generate some random data:

@WebServlet("/index.html")
public class MyTestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
        try {
            response.setContentType("text/plain");
            response.setCharacterEncoding("utf-8");

            byte[] bytesToWrite = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57};

            // Try to write 1,000,000 bytes to the client.
            try (OutputStream out = response.getOutputStream()) {
                for (int i = 0; i < 100000; i++) {
                    out.write(bytesToWrite);
                }
            }

        } catch (IOException ex) {
            // Ignore
        }
    }
}

Deploy the application as ROOT, so that the servlet will be reachable at "/".
Configure Tomcat to have an AJP-APR connector listening on port 8009.


2. On the client side, create a program like this:

public class AjpTester {
    public static void main(String[] args) throws IOException {

        byte[] ajpPacketBytes = { // JK_AJP13_FORWARD_REQUEST for "/"
            0x12, 0x34, 0x00, 0x54, 0x02, 0x02, 0x00, 0x08,
            0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
            0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x07, 0x31,
            0x2e, 0x32, 0x2e, 0x33, 0x2e, 0x34, 0x00, 0x00,
            0x07, 0x31, 0x2e, 0x32, 0x2e, 0x33, 0x2e, 0x34,
            0x00, 0x00, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
            0x68, 0x6f, 0x73, 0x74, 0x00, 0x00, 0x50, 0x00,
            0x00, 0x02, (byte)0xa0, 0x06, 0x00, 0x0a, 0x4b, 0x65,
            0x65, 0x70, 0x2d, 0x41, 0x6c, 0x69, 0x76, 0x65,
            0x00, (byte)0xa0, 0x0b, 0x00, 0x09, 0x6c, 0x6f, 0x63,
            0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x00, (byte)0xff
        };

        Socket s = new Socket("localhost", 8009); // connect to AJP port
        OutputStream sout = s.getOutputStream();
        InputStream sin = s.getInputStream();

        sout.write(ajpPacketBytes); // request Url "/"

        // Read between 10,000 and 11,023 bytes, then exit the JVM
        byte[] buf = new byte[1024];
        int read;
        int bytesRead = 0;
        while ((read = sin.read(buf)) > 0) {
            bytesRead += read;
            if (bytesRead >= 10000) {
                System.exit(1);
            }
        }
    }
}

The client will send an JK_AJP13_FORWARD_REQUEST message which requests URL "/"
and sends "Connection: keep-alive" and "Host: localhost" headers.


3. Tomcat will display this exception:

Apr 20, 2012 10:57:08 PM org.apache.coyote.ajp.AjpAprProcessor process
Schwerwiegend: Error processing request
java.nio.BufferOverflowException
    at java.nio.DirectByteBuffer.put(DirectByteBuffer.java:357)
    at org.apache.coyote.ajp.AjpAprProcessor.output(AjpAprProcessor.java:285)
    at
org.apache.coyote.ajp.AbstractAjpProcessor$SocketOutputBuffer.doWrite(AbstractAjpProcessor.java:1119)
    at org.apache.coyote.Response.doWrite(Response.java:504)
    at
org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:383)
    at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:462)
    at
org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:334)
    at org.apache.catalina.connector.OutputBuffer.close(OutputBuffer.java:283)
    at org.apache.catalina.connector.Response.finishResponse(Response.java:511)
    at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:434)
    at org.apache.coyote.ajp.AjpAprProcessor.process(AjpAprProcessor.java:197)
    at
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
    at
org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1812)
    at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)


I guess the exception itself is harmless, but it may fill up log files, if
clients disconnect frequently while receiving data.
Note that before that exception occurs, a regular "ClientAbortException: 
java.io.IOException: Failed to send AJP message" is thrown by
org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:388).
The exception occurs neither with AJP-BIO nor with AJP-NIO.


[1] http://markmail.org/message/zogi3tfbz2kyw3jg
[2] http://markmail.org/message/ntejdw36pfhlqodr

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

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

Reply via email to