Skip to content

Commit

Permalink
Expect fail when serialize inf and nan for Value.number_value in json…
Browse files Browse the repository at this point in the history
… format. fixes #11259

Implemented in java, c++, python and upb. Also added conformance test.

https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Value
where it says:
attempting to serialize NaN or Infinity results in error. (We can't serialize these as string "NaN" or "Infinity" values like we do for regular fields, because they would parse as string_value, not number_value).

PiperOrigin-RevId: 500139380
  • Loading branch information
anandolee authored and Copybara-Service committed Jan 6, 2023
1 parent 8eb8c2b commit c105e85
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 4 deletions.
4 changes: 4 additions & 0 deletions conformance/binary_json_conformance_suite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3334,6 +3334,10 @@ void BinaryAndJsonConformanceSuite::RunJsonTestsForValue() {
return value.empty();
},
true);
ExpectSerializeFailureForJson("ValueRejectNanNumberValue", RECOMMENDED,
"optional_value: { number_value: nan}");
ExpectSerializeFailureForJson("ValueRejectInfNumberValue", RECOMMENDED,
"optional_value: { number_value: inf}");
}

void BinaryAndJsonConformanceSuite::RunJsonTestsForAny() {
Expand Down
2 changes: 2 additions & 0 deletions conformance/failure_list_ruby.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT32.PackedInput.UnpackedOu
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT32.UnpackedInput.UnpackedOutput.ProtobufOutput
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.PackedInput.UnpackedOutput.ProtobufOutput
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.UnpackedInput.UnpackedOutput.ProtobufOutput
Recommended.ValueRejectInfNumberValue.JsonOutput
Recommended.ValueRejectNanNumberValue.JsonOutput
16 changes: 12 additions & 4 deletions java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.Type;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.DoubleValue;
Expand Down Expand Up @@ -966,7 +965,16 @@ private void printValue(MessageOrBuilder message) throws IOException {
throw new InvalidProtocolBufferException("Invalid Value type.");
}
for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
printSingleFieldValue(entry.getKey(), entry.getValue());
FieldDescriptor field = entry.getKey();
if (field.getType() == FieldDescriptor.Type.DOUBLE) {
Double doubleValue = (Double) entry.getValue();
if (doubleValue.isNaN() || doubleValue.isInfinite()) {
throw new IllegalArgumentException(
"google.protobuf.Value cannot encode double values for "
+ "infinity or nan, because they would be parsed as a string.");
}
}
printSingleFieldValue(field, entry.getValue());
}
}

Expand Down Expand Up @@ -1683,7 +1691,7 @@ private void mergeMapField(FieldDescriptor field, JsonElement json, Message.Buil
Object key = parseFieldValue(keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
Object value = parseFieldValue(valueField, entry.getValue(), entryBuilder);
if (value == null) {
if (ignoringUnknownFields && valueField.getType() == Type.ENUM) {
if (ignoringUnknownFields && valueField.getType() == FieldDescriptor.Type.ENUM) {
continue;
} else {
throw new InvalidProtocolBufferException("Map value cannot be null.");
Expand Down Expand Up @@ -1724,7 +1732,7 @@ private void mergeRepeatedField(
for (int i = 0; i < array.size(); ++i) {
Object value = parseFieldValue(field, array.get(i), builder);
if (value == null) {
if (ignoringUnknownFields && field.getType() == Type.ENUM) {
if (ignoringUnknownFields && field.getType() == FieldDescriptor.Type.ENUM) {
continue;
} else {
throw new InvalidProtocolBufferException(
Expand Down
12 changes: 12 additions & 0 deletions src/google/protobuf/json/internal/unparser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <complex>
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <sstream>
#include <string>
Expand Down Expand Up @@ -489,6 +490,17 @@ absl::Status WriteValue(JsonWriter& writer, const Msg<Traits>& msg,
if (Traits::GetSize(number_field, msg) > 0) {
auto x = Traits::GetDouble(number_field, msg);
RETURN_IF_ERROR(x.status());
if (std::isnan(*x)) {
return absl::InvalidArgumentError(
"google.protobuf.Value cannot encode double values for nan, "
"because it would be parsed as a string");
}
if (*x == std::numeric_limits<double>::infinity() |
*x == -std::numeric_limits<double>::infinity()) {
return absl::InvalidArgumentError(
"google.protobuf.Value cannot encode double values for "
"infinity, because it would be parsed as a string");
}
writer.Write(*x);
return absl::OkStatus();
}
Expand Down

0 comments on commit c105e85

Please sign in to comment.