Skip to content

feat(jsonrpc): add resource restrict for jsonrpc#6728

Open
317787106 wants to merge 21 commits intotronprotocol:developfrom
317787106:hotfix/restrict_jsonrpc_size
Open

feat(jsonrpc): add resource restrict for jsonrpc#6728
317787106 wants to merge 21 commits intotronprotocol:developfrom
317787106:hotfix/restrict_jsonrpc_size

Conversation

@317787106
Copy link
Copy Markdown
Collaborator

@317787106 317787106 commented Apr 28, 2026

What does this PR do?

Adds configurable resource limits to the JSON-RPC endpoint to prevent memory exhaustion and abuse from oversized requests or responses. Closes #6632

Changes:

  1. Batch size limit (node.jsonrpc.maxBatchSize, default: 100)

    • Validates the array length of batch JSON-RPC requests before dispatching.
    • Requests exceeding the limit are rejected with error code -32005 (exceed limit).
    • The check is skipped when maxBatchSize ≤ 0 (no limit).
  2. Empty batch rejection

    • An empty batch array [] is now rejected with error code -32600 (Invalid Request) per JSON-RPC 2.0 §6: "the response from the Server MUST be a single Response object" when the input is not an array with at least one value.
    • The response is a single error object (not an array), matching the spec requirement.
  3. Response size limit (node.jsonrpc.maxResponseSize, default: 25 MB)

    • Introduces BufferedResponseWrapper: intercepts getOutputStream() and getWriter() writes into an in-memory buffer. When a write would exceed the configured limit, it sets an overflow flag and resets the buffer instead of continuing to accumulate bytes, bounding worst-case memory usage to at most maxResponseSize.
    • Introduces CachedBodyRequestWrapper: replays the pre-read request body via both getInputStream() and getReader(), so the body can be inspected before being forwarded to JsonRpcServer.
    • After the handler returns, the servlet checks isOverflow() and — if set — discards the partial buffer and returns error code -32003 (response too large).
  4. Address list limit (node.jsonrpc.maxAddressSize, default: 1000)

    • In LogFilter, validates the address array length in eth_getLogs / eth_newFilter requests.
    • Requests exceeding the limit are rejected with JsonRpcInvalidParamsException.
  5. Structured JSON-RPC error responses

    • writeJsonRpcError uses ObjectMapper to build error responses safely, avoiding JSON injection from error messages.
    • Error codes follow the JSON-RPC 2.0 spec:
    • -32700 parse error
    • -32600 invalid request
    • -32603 internal error
    • -32005 exceed limit
    • -32003 response too large

Why are these changes required?

  • Without limits, a client can send an arbitrarily large batch, trigger an expensive query with many addresses, or force the node to serialize a massive response — all of which cause unbounded memory growth.
  • The response buffer caps worst-case allocation to maxResponseSize and fails fast rather than buffering the entire response before checking.
  • Rejecting [] closes a spec compliance gap: previously the empty batch fell through to JsonRpcServer, whose behavior for an empty array is undefined by the spec.

Configuration

node {
  jsonrpc {
    # Max JSON-RPC batch array size; 0 = no limit
    maxBatchSize = 100
    # Max response body in bytes (default 25 MB)
    maxResponseSize = 26214400
    # Max address entries in eth_getLogs / eth_newFilter
    maxAddressSize = 1000
  }
}

Tests

Test class Coverage
JsonRpcServletTest Parse error, empty body, empty batch []-32600, batch size limit, response overflow, internal error, normal path
BufferedResponseWrapperTest Write, overflow, reset, getWriter delegation
CachedBodyRequestWrapperTest Body replay via getInputStream and getReader
JsonRpcTest.testLogFilterAddressSizeLimit Address list at limit (passes), at limit+1 (throws with "exceed max addresses:"), limit=0 disabled

@halibobo1205 halibobo1205 added this to the GreatVoyage-v4.8.2 milestone Apr 29, 2026
@halibobo1205 halibobo1205 added topic:json-rpc topic:api rpc/http related issue labels Apr 29, 2026
Comment thread common/src/main/java/org/tron/core/config/args/NodeConfig.java
Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcServlet.java Outdated
Comment thread common/src/main/java/org/tron/common/parameter/CommonParameter.java
Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcServlet.java Outdated
Comment thread framework/src/main/resources/config.conf Outdated
Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcServlet.java Outdated
@github-actions github-actions Bot requested review from 0xbigapple and bladehan1 May 8, 2026 07:39
import javax.servlet.http.HttpServletRequestWrapper;

/**
* Wraps a request and replays a pre-read body from a byte array.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] Document the supported scope of CachedBodyRequestWrapper in the class javadoc

The wrapper currently works correctly for what JsonRpcServlet.doPost needs — synchronous JSON-body POST, body read via getInputStream() / getReader(). It is, however, placed in services/filter/ (alongside Filter implementations) and named generically, which invites future reuse. Several HttpServletRequest capabilities are silently incompatible with a cached byte[] body and are easy to overlook:

  • application/x-www-form-urlencoded — the container consumes the original stream when getParameter* is called; once the body is cached, those accessors return null/empty.
  • multipart/*getPart() / getParts() read from the original (already-consumed) stream.
  • async non-blocking I/O — setReadListener cannot be honored over a ByteArrayInputStream.
  • request dispatch / forward chains — the cached body does not propagate through dispatchers in a meaningful way.

Making the scope explicit in the class javadoc is the single cheapest hedge against this wrapper being moved to a generic filter or reused on a different content-type later. Suggested wording (feel free to adjust):

/**
 * Wraps a request to replay a pre-read body from a byte array,
 * allowing the body to be read more than once.
 *
 * <p><b>Scope:</b> designed for synchronous, raw-body POST endpoints
 * (e.g. JSON-RPC). It is NOT compatible with:
 * <ul>
 *   <li>{@code application/x-www-form-urlencoded} — cached body cannot back
 *       {@code getParameter*}.</li>
 *   <li>multipart — {@code getPart()/getParts()} read from the original
 *       (already-consumed) stream.</li>
 *   <li>async non-blocking I/O — see {@code setReadListener}.</li>
 *   <li>request dispatch / forward chains.</li>
 * </ul>
 *
 * <p>Multiple calls to {@code getInputStream()} (or {@code getReader()})
 * are allowed and each returns a fresh stream over the same cached body —
 * a deliberate extension of the standard servlet contract.
 */

Not blocking. Pure documentation, no behavior change.

}

@Override
public void setReadListener(ReadListener readListener) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] Make setReadListener fail-fast instead of a silent no-op

A cached byte[] body cannot drive async non-blocking I/O — the registered ReadListener would never receive onDataAvailable() callbacks against a ByteArrayInputStream snapshot. The current empty implementation accepts the listener silently, so a future caller that turns this servlet into an async path would hang with no visible error.

jsonrpc4j today is fully synchronous, so this is latent rather than active. Throwing fail-fast here costs one line and surfaces a future regression immediately at the right place.

@Override
public void setReadListener(ReadListener readListener) {
  throw new UnsupportedOperationException(
      "async I/O is not supported on cached body");
}

The existing isReady() returning true is fine — for an in-memory stream that is the honest answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:api rpc/http related issue topic:json-rpc

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[Feature] Introduce resource limits for JSON-RPC (batch size, response size, address size, timeout)

5 participants