Skip to content

Commit d24be29

Browse files
authored
Merge pull request #26434 from protocolbuffers/salo-33.x
Avoid toBigIntegerExact in JsonFormat to avoid degenerate parse behav…
2 parents 2ba9b9c + e515811 commit d24be29

2 files changed

Lines changed: 56 additions & 9 deletions

File tree

java/util/src/main/java/com/google/protobuf/util/JsonFormat.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,11 +1797,13 @@ private int parseUint32(JsonElement json) throws InvalidProtocolBufferException
17971797
// "1.000" are treated as equal in JSON. For this reason we accept floating point values for
17981798
// integer fields as well as long as it actually is an integer (i.e., round(value) == value).
17991799
try {
1800-
BigDecimal decimalValue = new BigDecimal(json.getAsString());
1801-
BigInteger value = decimalValue.toBigIntegerExact();
1800+
BigDecimal value = new BigDecimal(json.getAsString());
18021801
if (value.signum() < 0 || value.compareTo(MAX_UINT32) > 0) {
18031802
throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
18041803
}
1804+
if (value.remainder(BigDecimal.ONE).signum() != 0) {
1805+
throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
1806+
}
18051807
return value.intValue();
18061808
} catch (RuntimeException e) {
18071809
InvalidProtocolBufferException ex = new InvalidProtocolBufferException(
@@ -1811,16 +1813,19 @@ private int parseUint32(JsonElement json) throws InvalidProtocolBufferException
18111813
}
18121814
}
18131815

1814-
private static final BigInteger MAX_UINT32 = new BigInteger("FFFFFFFF", 16);
1815-
private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
1816+
private static final BigDecimal MAX_UINT32 = new BigDecimal(0xFFFFFFFFL);
1817+
private static final BigDecimal MAX_UINT64 =
1818+
new BigDecimal(new BigInteger("FFFFFFFFFFFFFFFF", 16));
18161819

18171820
private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
18181821
try {
1819-
BigDecimal decimalValue = new BigDecimal(json.getAsString());
1820-
BigInteger value = decimalValue.toBigIntegerExact();
1821-
if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
1822+
BigDecimal value = new BigDecimal(json.getAsString());
1823+
if (value.signum() < 0 || value.compareTo(MAX_UINT64) > 0) {
18221824
throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
18231825
}
1826+
if (value.remainder(BigDecimal.ONE).signum() != 0) {
1827+
throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
1828+
}
18241829
return value.longValue();
18251830
} catch (RuntimeException e) {
18261831
InvalidProtocolBufferException ex = new InvalidProtocolBufferException(

java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,6 @@ public void testRejectFractionalUnsignedInt32() throws IOException {
391391
assertWithMessage("Exception is expected.").fail();
392392
} catch (InvalidProtocolBufferException expected) {
393393
assertThat(expected).hasMessageThat().isEqualTo("Not an uint32 value: 1.5");
394-
assertThat(expected).hasCauseThat().isNotNull();
395394
}
396395
}
397396

@@ -403,7 +402,50 @@ public void testRejectFractionalUnsignedInt64() throws IOException {
403402
assertWithMessage("Exception is expected.").fail();
404403
} catch (InvalidProtocolBufferException expected) {
405404
assertThat(expected).hasMessageThat().isEqualTo("Not an uint64 value: 1.5");
406-
assertThat(expected).hasCauseThat().isNotNull();
405+
}
406+
}
407+
408+
@Test
409+
public void testRejectLargeQuotedExponentInt32() throws IOException {
410+
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
411+
try {
412+
mergeFromJson("{\"optionalInt32\": \"1e536870000\"}", builder);
413+
assertWithMessage("Exception is expected.").fail();
414+
} catch (InvalidProtocolBufferException expected) {
415+
assertThat(expected).hasMessageThat().isEqualTo("Not an int32 value: \"1e536870000\"");
416+
}
417+
}
418+
419+
@Test
420+
public void testRejectLargeQuotedExponentUnsignedUint32() throws IOException {
421+
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
422+
try {
423+
mergeFromJson("{\"optionalUint32\": \"1e536870000\"}", builder);
424+
assertWithMessage("Exception is expected.").fail();
425+
} catch (InvalidProtocolBufferException expected) {
426+
assertThat(expected).hasMessageThat().isEqualTo("Out of range uint32 value: \"1e536870000\"");
427+
}
428+
}
429+
430+
@Test
431+
public void testRejectLargeQuotedExponentInt64() throws IOException {
432+
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
433+
try {
434+
mergeFromJson("{\"optionalInt64\": \"1e536870000\"}", builder);
435+
assertWithMessage("Exception is expected.").fail();
436+
} catch (InvalidProtocolBufferException expected) {
437+
assertThat(expected).hasMessageThat().isEqualTo("Not an int64 value: \"1e536870000\"");
438+
}
439+
}
440+
441+
@Test
442+
public void testRejectLargeQuotedExponentUnsignedUint64() throws IOException {
443+
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
444+
try {
445+
mergeFromJson("{\"optionalUint64\": \"1e536870000\"}", builder);
446+
assertWithMessage("Exception is expected.").fail();
447+
} catch (InvalidProtocolBufferException expected) {
448+
assertThat(expected).hasMessageThat().isEqualTo("Out of range uint64 value: \"1e536870000\"");
407449
}
408450
}
409451

0 commit comments

Comments
 (0)