Author: markt
Date: Sun Mar  8 17:41:41 2015
New Revision: 1665061

URL: http://svn.apache.org/r1665061
Log:
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=57674
Avoid a BufferOverflowException when an AJP response body chunk larger than the 
socket write buffer is being written. This typically requires a larger than 
default AJP packetSize.

Modified:
    tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNio2Processor.java
    tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java
    
tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java
    tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml

Modified: tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNio2Processor.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNio2Processor.java?rev=1665061&r1=1665060&r2=1665061&view=diff
==============================================================================
--- tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNio2Processor.java 
(original)
+++ tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNio2Processor.java Sun 
Mar  8 17:41:41 2015
@@ -142,11 +142,12 @@ public class AjpNio2Processor extends Ab
 
         ByteBuffer writeBuffer =
                 socketWrapper.getSocket().getBufHandler().getWriteBuffer();
+        int toWrite = Math.min(length, writeBuffer.remaining());
 
         int result = 0;
         if (block) {
             writeBuffer.clear();
-            writeBuffer.put(src, offset, length);
+            writeBuffer.put(src, offset, toWrite);
             writeBuffer.flip();
             try {
                 result = socketWrapper.getSocket().write(writeBuffer)
@@ -159,14 +160,14 @@ public class AjpNio2Processor extends Ab
             synchronized (writeCompletionHandler) {
                 if (!writePending) {
                     writeBuffer.clear();
-                    writeBuffer.put(src, offset, length);
+                    writeBuffer.put(src, offset, toWrite);
                     writeBuffer.flip();
                     writePending = true;
                     Nio2Endpoint.startInline();
                     socketWrapper.getSocket().write(writeBuffer, 
socketWrapper.getTimeout(),
                             TimeUnit.MILLISECONDS, socketWrapper, 
writeCompletionHandler);
                     Nio2Endpoint.endInline();
-                    result = length;
+                    result = toWrite;
                 }
             }
         }

Modified: tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java?rev=1665061&r1=1665060&r2=1665061&view=diff
==============================================================================
--- tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java 
(original)
+++ tomcat/tc8.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java Sun 
Mar  8 17:41:41 2015
@@ -124,7 +124,8 @@ public class AjpNioProcessor extends Abs
         ByteBuffer writeBuffer =
                 socketWrapper.getSocket().getBufHandler().getWriteBuffer();
 
-        writeBuffer.put(src, offset, length);
+        int toWrite = Math.min(length, writeBuffer.remaining());
+        writeBuffer.put(src, offset, toWrite);
 
         writeBuffer.flip();
 

Modified: 
tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java?rev=1665061&r1=1665060&r2=1665061&view=diff
==============================================================================
--- 
tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java 
(original)
+++ 
tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java 
Sun Mar  8 17:41:41 2015
@@ -765,6 +765,48 @@ public class TestAbstractAjpProcessor ex
     }
 
 
+    @Test
+    public void testLargeResponse() throws Exception {
+
+        int ajpPacketSize = 16000;
+
+        Tomcat tomcat = getTomcatInstance();
+        tomcat.getConnector().setProperty("packetSize", 
Integer.toString(ajpPacketSize));
+
+        // No file system docBase required
+        Context ctx = tomcat.addContext("", null);
+
+        FixedResponseSizeServlet servlet = new FixedResponseSizeServlet(15000, 
16000);
+        Tomcat.addServlet(ctx, "FixedResponseSizeServlet", servlet);
+        ctx.addServletMapping("/", "FixedResponseSizeServlet");
+
+        tomcat.start();
+
+        SimpleAjpClient ajpClient = new SimpleAjpClient(ajpPacketSize);
+        ajpClient.setPort(getPort());
+        ajpClient.connect();
+
+        validateCpong(ajpClient.cping());
+
+        ajpClient.setUri("/");
+        TesterAjpMessage forwardMessage = ajpClient.createForwardMessage();
+        forwardMessage.end();
+
+        TesterAjpMessage responseHeaders = 
ajpClient.sendMessage(forwardMessage);
+
+        // Expect 3 messages: headers, body, end for a valid request
+        validateResponseHeaders(responseHeaders, 200, "OK");
+        TesterAjpMessage responseBody = ajpClient.readMessage();
+        Assert.assertTrue(responseBody.len > 15000);
+        validateResponseEnd(ajpClient.readMessage(), true);
+
+        // Double check the connection is still open
+        validateCpong(ajpClient.cping());
+
+        ajpClient.disconnect();
+    }
+
+
     /**
      * Process response header packet and checks the status. Any other data is
      * ignored.
@@ -945,4 +987,33 @@ public class TestAbstractAjpProcessor ex
             }
         }
     }
+
+
+    private static class FixedResponseSizeServlet extends HttpServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        private final int responseSize;
+        private final int bufferSize;
+
+        public FixedResponseSizeServlet(int responseSize, int bufferSize) {
+            this.responseSize = responseSize;
+            this.bufferSize = bufferSize;
+        }
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            resp.setBufferSize(bufferSize);
+
+            resp.setContentType("text/plain");
+            resp.setCharacterEncoding("UTF-8");
+            resp.setContentLength(responseSize);
+
+            PrintWriter pw = resp.getWriter();
+            for (int i = 0; i < responseSize; i++) {
+                pw.append('X');
+            }
+        }
+    }
 }

Modified: tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml?rev=1665061&r1=1665060&r2=1665061&view=diff
==============================================================================
--- tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml Sun Mar  8 17:41:41 2015
@@ -139,6 +139,11 @@
         body chunk larger than the socket read buffer is being read. This
         typically requires a larger than default AJP packetSize. (markt)
       </fix>
+      <fix>
+        <bug>57674</bug>: Avoid a BufferOverflowException when an AJP response
+        body chunk larger than the socket write buffer is being written. This
+        typically requires a larger than default AJP packetSize. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">



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

Reply via email to