Skip to content

Commit 89e6302

Browse files
authored
Merge pull request #26433 from protocolbuffers/salo-34.x
Avoid toBigIntegerExact in JsonFormat to avoid degenerate parse behavior in the face of large exponents
2 parents c7b3cfa + ab3d7f0 commit 89e6302

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
@@ -1792,11 +1792,13 @@ private int parseUint32(JsonElement json) throws InvalidProtocolBufferException
17921792
// "1.000" are treated as equal in JSON. For this reason we accept floating point values for
17931793
// integer fields as well as long as it actually is an integer (i.e., round(value) == value).
17941794
try {
1795-
BigDecimal decimalValue = new BigDecimal(json.getAsString());
1796-
BigInteger value = decimalValue.toBigIntegerExact();
1795+
BigDecimal value = new BigDecimal(json.getAsString());
17971796
if (value.signum() < 0 || value.compareTo(MAX_UINT32) > 0) {
17981797
throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
17991798
}
1799+
if (value.remainder(BigDecimal.ONE).signum() != 0) {
1800+
throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
1801+
}
18001802
return value.intValue();
18011803
} catch (RuntimeException e) {
18021804
InvalidProtocolBufferException ex = new InvalidProtocolBufferException(
@@ -1806,16 +1808,19 @@ private int parseUint32(JsonElement json) throws InvalidProtocolBufferException
18061808
}
18071809
}
18081810

1809-
private static final BigInteger MAX_UINT32 = new BigInteger("FFFFFFFF", 16);
1810-
private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
1811+
private static final BigDecimal MAX_UINT32 = new BigDecimal(0xFFFFFFFFL);
1812+
private static final BigDecimal MAX_UINT64 =
1813+
new BigDecimal(new BigInteger("FFFFFFFFFFFFFFFF", 16));
18111814

18121815
private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
18131816
try {
1814-
BigDecimal decimalValue = new BigDecimal(json.getAsString());
1815-
BigInteger value = decimalValue.toBigIntegerExact();
1816-
if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
1817+
BigDecimal value = new BigDecimal(json.getAsString());
1818+
if (value.signum() < 0 || value.compareTo(MAX_UINT64) > 0) {
18171819
throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
18181820
}
1821+
if (value.remainder(BigDecimal.ONE).signum() != 0) {
1822+
throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
1823+
}
18191824
return value.longValue();
18201825
} catch (RuntimeException e) {
18211826
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)