From 039f71a04ba91bb336831abc07f87a422269accf Mon Sep 17 00:00:00 2001
From: Michael Mullins <221403458+alphacharlie-dev@users.noreply.github.com>
Date: Fri, 31 Jul 2026 20:19:17 +0200
Subject: [PATCH] bound the request body size read by RPCServletUtils
readContent copies the request InputStream into a ByteArrayOutputStream in
a loop that only stops at end-of-stream, then materialises the result as a
String, so peak memory is roughly twice the body size. No Content-Length
ceiling and no running byte total are applied anywhere on the path, and the
method is reached without authentication from both GWT-RPC
(readContentAsGwtRpc) and RequestFactory (RequestFactoryServlet), so a
single large POST can exhaust the heap. Servlet container max-post-size
settings do not usually cover text/x-gwt-rpc or application/json.
Reject up front when Content-Length already exceeds the limit, and keep a
running total inside the loop so chunked transfers and an understated
header are bounded as well. The limit defaults to 1 MiB and can be raised
with the gwt.rpc.maxRequestBodyBytes system property; an unparseable value
falls back to the default rather than disabling the check.
Applications that send payloads larger than 1 MiB will need to set that
property.
---
.../gwt/user/server/rpc/RPCServletUtils.java | 53 ++++++++++++++++++-
1 file changed, 51 insertions(+), 2 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
index ffcaad73ee..8a44634661 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
@@ -52,6 +52,40 @@ public class RPCServletUtils {
*/
static final int BUFFER_SIZE = 4096;
+ /**
+ * Name of the system property that overrides the maximum accepted request body size, in
+ * bytes. Values that are not a positive integer are ignored in favour of the default, so a
+ * malformed override cannot disable the cap.
+ */
+ public static final String MAX_REQUEST_BODY_BYTES_PROPERTY = "gwt.rpc.maxRequestBodyBytes";
+
+ /**
+ * Default maximum request body size accepted by {@link #readContent}, in bytes.
+ */
+ static final int DEFAULT_MAX_REQUEST_BODY_BYTES = 1024 * 1024;
+
+ /**
+ * Maximum number of bytes {@link #readContent} buffers from a request body before rejecting
+ * it. Without a ceiling the read loop buffers an arbitrarily large attacker-supplied body
+ * into memory and then duplicates it as a String, exhausting the heap (CWE-400).
+ */
+ static final int MAX_REQUEST_BODY_BYTES = readMaxRequestBodyBytes();
+
+ private static int readMaxRequestBodyBytes() {
+ String override = System.getProperty(MAX_REQUEST_BODY_BYTES_PROPERTY);
+ if (override != null) {
+ try {
+ int parsed = Integer.parseInt(override.trim());
+ if (parsed > 0) {
+ return parsed;
+ }
+ } catch (NumberFormatException e) {
+ // Fall through to the default rather than leaving the body unbounded.
+ }
+ }
+ return DEFAULT_MAX_REQUEST_BODY_BYTES;
+ }
+
private static final String ACCEPT_ENCODING = "Accept-Encoding";
private static final String ATTACHMENT = "attachment";
@@ -201,7 +235,8 @@ public static boolean isExpectedException(Method serviceIntfMethod,
* from or closed
* @throws ServletException if the request's content type does not
* equal the supplied expectedContentType or
- * expectedCharSet
+ * expectedCharSet, or if the request body exceeds
+ * {@link #MAX_REQUEST_BODY_BYTES}
*/
public static String readContent(HttpServletRequest request,
String expectedContentType, String expectedCharSet)
@@ -215,17 +250,31 @@ public static String readContent(HttpServletRequest request,
/*
* Need to support 'Transfer-Encoding: chunked', so do not rely on
- * presence of a 'Content-Length' request header.
+ * presence of a 'Content-Length' request header. When the header is present and already
+ * over the limit, reject before reading any of the body; the accumulated-byte check below
+ * is what enforces the limit for chunked requests and for an understated header.
*/
+ int declaredLength = request.getContentLength();
+ if (declaredLength > MAX_REQUEST_BODY_BYTES) {
+ throw new ServletException("Request body of " + declaredLength
+ + " bytes exceeds the maximum of " + MAX_REQUEST_BODY_BYTES + " bytes");
+ }
+
InputStream in = request.getInputStream();
byte[] buffer = new byte[BUFFER_SIZE];
ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
try {
+ int totalRead = 0;
while (true) {
int byteCount = in.read(buffer);
if (byteCount == -1) {
break;
}
+ totalRead += byteCount;
+ if (totalRead > MAX_REQUEST_BODY_BYTES) {
+ throw new ServletException("Request body exceeds the maximum of "
+ + MAX_REQUEST_BODY_BYTES + " bytes");
+ }
out.write(buffer, 0, byteCount);
}
return new String(out.toByteArray(), getCharset(expectedCharSet));