This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 3bcf5f2be743d030e08cdaa22155f9397865a252 Author: opencode <[email protected]> AuthorDate: Sat Oct 17 11:38:36 2026 +0200 Validate the WebSocket maximum message buffer size The session setters setMaxBinaryMessageBufferSize() / setMaxTextMessageBufferSize() and the container default setters setDefaultMaxBinaryMessageBufferSize() / setDefaultMaxTextMessageBufferSize() accepted any int without validation. With a zero size and a MessageHandler.Partial registered, the frame receiver enters an unbounded busy loop: the terminal transformation always reports OVERFLOW because the destination buffer has no room, so the application handler is invoked repeatedly with empty buffers and the frame payload is never consumed. A negative size results in a NegativeArraySizeException when the message buffer is allocated. Reject values below one with an IllegalArgumentException in the four setters. Since the session initializes its sizes from the container defaults and the server context init parameters are applied through the container setters, this covers all configuration paths. Add a regression test covering the container and session setters. --- .../tomcat/websocket/LocalStrings.properties | 2 + java/org/apache/tomcat/websocket/WsSession.java | 6 +++ .../tomcat/websocket/WsWebSocketContainer.java | 8 ++++ .../tomcat/websocket/TestWsWebSocketContainer.java | 47 ++++++++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties index ca80062af0..5536b82290 100644 --- a/java/org/apache/tomcat/websocket/LocalStrings.properties +++ b/java/org/apache/tomcat/websocket/LocalStrings.properties @@ -132,6 +132,7 @@ wsSession.duplicateHandlerBinary=A binary message handler has already been confi wsSession.duplicateHandlerPong=A pong message handler has already been configured wsSession.duplicateHandlerText=A text message handler has already been configured wsSession.flushFailOnClose=Failed to flush batched messages on session close +wsSession.illegalBufferSize=Invalid maximum message buffer size [{0}]. The value must be greater than zero. wsSession.instanceCreateFailed=Endpoint instance creation failed wsSession.instanceNew=Endpoint instance registration failed wsSession.invalidHandlerTypePong=A pong message handler must implement MessageHandler.Whole @@ -155,6 +156,7 @@ wsWebSocketContainer.connect.write=Writing the HTTP upgrade request from buffer wsWebSocketContainer.defaultConfiguratorFail=Failed to create the default configurator wsWebSocketContainer.failedAuthentication=Failed to handle HTTP response code [{0}]. [{1}] header was not accepted by server. wsWebSocketContainer.httpRequestFailed=The HTTP request to initiate the WebSocket connection to [{0}] failed +wsWebSocketContainer.illegalBufferSize=Invalid default maximum message buffer size [{0}]. The value must be greater than zero. wsWebSocketContainer.invalidExtensionsHeader=The server sent an invalid extension header wsWebSocketContainer.invalidExtensionParameters=The server responded with extension parameters the client is unable to support wsWebSocketContainer.invalidHeader=Unable to parse HTTP header as no colon is present to delimit header name and header value in [{0}]. The header has been skipped. diff --git a/java/org/apache/tomcat/websocket/WsSession.java b/java/org/apache/tomcat/websocket/WsSession.java index c8690ad44a..9f3ed1c01a 100644 --- a/java/org/apache/tomcat/websocket/WsSession.java +++ b/java/org/apache/tomcat/websocket/WsSession.java @@ -495,6 +495,9 @@ public class WsSession implements Session { @Override public void setMaxBinaryMessageBufferSize(int max) { checkState(); + if (max < 1) { + throw new IllegalArgumentException(sm.getString("wsSession.illegalBufferSize", Integer.valueOf(max))); + } this.maxBinaryMessageBufferSize = max; } @@ -509,6 +512,9 @@ public class WsSession implements Session { @Override public void setMaxTextMessageBufferSize(int max) { checkState(); + if (max < 1) { + throw new IllegalArgumentException(sm.getString("wsSession.illegalBufferSize", Integer.valueOf(max))); + } this.maxTextMessageBufferSize = max; } diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java index 212451fe4e..cda0f24783 100644 --- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java +++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java @@ -1022,6 +1022,10 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce @Override public void setDefaultMaxBinaryMessageBufferSize(int max) { + if (max < 1) { + throw new IllegalArgumentException( + sm.getString("wsWebSocketContainer.illegalBufferSize", Integer.valueOf(max))); + } maxBinaryMessageBufferSize = max; } @@ -1034,6 +1038,10 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce @Override public void setDefaultMaxTextMessageBufferSize(int max) { + if (max < 1) { + throw new IllegalArgumentException( + sm.getString("wsWebSocketContainer.illegalBufferSize", Integer.valueOf(max))); + } maxTextMessageBufferSize = max; } diff --git a/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java b/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java index 21e332596f..ce42d64a85 100644 --- a/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java +++ b/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java @@ -25,6 +25,7 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.IntConsumer; import jakarta.servlet.ServletContextEvent; import jakarta.websocket.ClientEndpointConfig; @@ -106,6 +107,52 @@ public class TestWsWebSocketContainer extends WsWebSocketContainerBaseTest { } + @Test + public void testMaxMessageBufferSizeValidation() throws Exception { + Tomcat tomcat = getTomcatInstance(); + // No file system docBase required + Context ctx = getProgrammaticRootContext(); + ctx.addApplicationListener(TesterEchoServer.Config.class.getName()); + Tomcat.addServlet(ctx, "default", new DefaultServlet()); + ctx.addServletMapping("/", "default"); + + tomcat.start(); + + WsWebSocketContainer wsContainer = new WsWebSocketContainer(); + + doTestIllegalBufferSize(wsContainer::setDefaultMaxBinaryMessageBufferSize); + doTestIllegalBufferSize(wsContainer::setDefaultMaxTextMessageBufferSize); + + Session wsSession = wsContainer.connectToServer(TesterProgrammaticEndpoint.class, + ClientEndpointConfig.Builder.create().build(), + new URI("ws://" + getHostName() + ":" + getPort() + TesterEchoServer.Config.PATH_ASYNC)); + + doTestIllegalBufferSize(wsSession::setMaxBinaryMessageBufferSize); + doTestIllegalBufferSize(wsSession::setMaxTextMessageBufferSize); + + // Positive values remain valid + wsSession.setMaxBinaryMessageBufferSize(1); + Assert.assertEquals(1, wsSession.getMaxBinaryMessageBufferSize()); + wsSession.setMaxTextMessageBufferSize(1); + Assert.assertEquals(1, wsSession.getMaxTextMessageBufferSize()); + + wsSession.close(); + wsContainer.destroy(); + } + + + private static void doTestIllegalBufferSize(IntConsumer setter) { + for (int size : new int[] { 0, -1 }) { + try { + setter.accept(size); + Assert.fail("No exception thrown for buffer size [" + size + "]"); + } catch (IllegalArgumentException iae) { + // Expected + } + } + } + + @Test(expected = DeploymentException.class) public void testConnectToServerEndpointInvalidScheme() throws Exception { Tomcat tomcat = getTomcatInstance(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
