Skip to content

java: limit numeric string length before BigDecimal parsing#26908

Closed
MindflareX wants to merge 2 commits into
protocolbuffers:mainfrom
MindflareX:fix/java-bigdecimal-length-check
Closed

java: limit numeric string length before BigDecimal parsing#26908
MindflareX wants to merge 2 commits into
protocolbuffers:mainfrom
MindflareX:fix/java-bigdecimal-length-check

Conversation

@MindflareX

Copy link
Copy Markdown
Contributor

Summary

BigDecimal(String) has O(N²) time complexity for N-digit strings on JDK versions before 18 (JDK-8291514). Five JSON parser methods — parseInt32, parseInt64, parseUint32, parseUint64, and parseDouble — pass user-controlled strings directly to new BigDecimal() without length validation.

A single JSON numeric value with 1,000,000 digits takes ~13 seconds to parse on JDK 17. This can be used to DoS any service that parses protobuf JSON messages with numeric fields from untrusted input.

Benchmark (JDK 17, x86-64 Linux)

Digits BigDecimal construction time
1,000 1.8 ms
10,000 6.3 ms
100,000 133 ms
1,000,000 13.1 seconds

Fix

Added a parseBigDecimal() helper that rejects strings longer than 1000 characters before constructing BigDecimal. This is generous — valid protobuf numeric values never exceed ~350 characters (Double.MAX_VALUE in non-scientific notation is ~309 digits).

Affected JDK versions

  • JDK 8, 11, 17 (all current LTS releases): Vulnerable — no built-in string length limit in BigDecimal
  • JDK 18+: JDK itself limits BigDecimal string input to 1100 characters by default (JDK-8291514), but the protobuf-level check is still worthwhile as defense-in-depth

Test

Added testParserRejectOverlyLongNumericStrings covering all 5 affected field types.

BigDecimal(String) has O(N^2) time complexity for N-digit strings on
JDK versions before 18 (JDK-8291514). The JSON parser passes
user-controlled strings directly to BigDecimal in parseInt32,
parseInt64, parseUint32, parseUint64, and parseDouble, allowing a
single long numeric value to consume seconds of CPU.

Add a parseBigDecimal helper that rejects strings longer than 1000
characters before constructing BigDecimal. This is generous — valid
protobuf numeric values never exceed ~350 characters.
@MindflareX MindflareX requested a review from a team as a code owner April 15, 2026 09:20
@MindflareX MindflareX requested review from googleberg and removed request for a team April 15, 2026 09:20
@esrauchg esrauchg added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 16, 2026
@github-actions github-actions Bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 16, 2026
public void testParserRejectOverlyLongNumericStrings() throws Exception {
// A numeric string with 10,000 digits should be rejected quickly to prevent
// O(N^2) BigDecimal parsing DoS.
String longNumber = "1" + "0".repeat(10000);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unfortunately you need to avoid using .repeat() here since our min support is java8 and .repeat() was only added after

Thanks!

String.repeat() was added in Java 11. Use a StringBuilder loop instead
to maintain Java 8 compatibility per project requirements.
@MindflareX MindflareX force-pushed the fix/java-bigdecimal-length-check branch from 53d5def to a461d0e Compare April 16, 2026 14:52
@MindflareX

Copy link
Copy Markdown
Contributor Author

Hi @esrauchg — thanks for the quick review! I replaced String.repeat() with a StringBuilder loop in a461d0e to keep Java 8
compat. PTAL when you get a chance.

@esrauchg esrauchg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

@esrauchg esrauchg removed the request for review from googleberg April 21, 2026 13:50
@esrauchg esrauchg added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 21, 2026
@github-actions github-actions Bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants