Index: third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc |
diff --git a/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc b/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc |
index 034d616ff18d4c50c392ed7bcb08d481d4643226..1f3781a4b48fc68ed4e9566a90798a33c06c8ea9 100644 |
--- a/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc |
+++ b/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc |
@@ -70,6 +70,9 @@ using util::Status; |
using util::StatusOr; |
namespace { |
+ |
+static int kDefaultMaxRecursionDepth = 64; |
+ |
// Finds a field with the given number. NULL if none found. |
const google::protobuf::Field* FindFieldByNumber( |
const google::protobuf::Type& type, int number); |
@@ -83,6 +86,29 @@ const google::protobuf::EnumValue* FindEnumValueByNumber( |
// Utility function to format nanos. |
const string FormatNanos(uint32 nanos); |
+ |
+StatusOr<string> MapKeyDefaultValueAsString( |
+ const google::protobuf::Field& field) { |
+ switch (field.kind()) { |
+ case google::protobuf::Field_Kind_TYPE_BOOL: |
+ return string("false"); |
+ case google::protobuf::Field_Kind_TYPE_INT32: |
+ case google::protobuf::Field_Kind_TYPE_INT64: |
+ case google::protobuf::Field_Kind_TYPE_UINT32: |
+ case google::protobuf::Field_Kind_TYPE_UINT64: |
+ case google::protobuf::Field_Kind_TYPE_SINT32: |
+ case google::protobuf::Field_Kind_TYPE_SINT64: |
+ case google::protobuf::Field_Kind_TYPE_SFIXED32: |
+ case google::protobuf::Field_Kind_TYPE_SFIXED64: |
+ case google::protobuf::Field_Kind_TYPE_FIXED32: |
+ case google::protobuf::Field_Kind_TYPE_FIXED64: |
+ return string("0"); |
+ case google::protobuf::Field_Kind_TYPE_STRING: |
+ return string(); |
+ default: |
+ return Status(util::error::INTERNAL, "Invalid map key type."); |
+ } |
+} |
} // namespace |
@@ -92,14 +118,23 @@ ProtoStreamObjectSource::ProtoStreamObjectSource( |
: stream_(stream), |
typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), |
own_typeinfo_(true), |
- type_(type) { |
+ type_(type), |
+ use_lower_camel_for_enums_(false), |
+ recursion_depth_(0), |
+ max_recursion_depth_(kDefaultMaxRecursionDepth) { |
GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; |
} |
ProtoStreamObjectSource::ProtoStreamObjectSource( |
google::protobuf::io::CodedInputStream* stream, const TypeInfo* typeinfo, |
const google::protobuf::Type& type) |
- : stream_(stream), typeinfo_(typeinfo), own_typeinfo_(false), type_(type) { |
+ : stream_(stream), |
+ typeinfo_(typeinfo), |
+ own_typeinfo_(false), |
+ type_(type), |
+ use_lower_camel_for_enums_(false), |
+ recursion_depth_(0), |
+ max_recursion_depth_(kDefaultMaxRecursionDepth) { |
GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; |
} |
@@ -238,9 +273,21 @@ StatusOr<uint32> ProtoStreamObjectSource::RenderMap( |
map_key = ReadFieldValueAsString(*field); |
} else if (field->number() == 2) { |
if (map_key.empty()) { |
- return Status(util::error::INTERNAL, "Map key must be non-empty"); |
+ // An absent map key is treated as the default. |
+ const google::protobuf::Field* key_field = |
+ FindFieldByNumber(*field_type, 1); |
+ if (key_field == NULL) { |
+ // The Type info for this map entry is incorrect. It should always |
+ // have a field named "key" and with field number 1. |
+ return Status(util::error::INTERNAL, "Invalid map entry."); |
+ } |
+ ASSIGN_OR_RETURN(map_key, MapKeyDefaultValueAsString(*key_field)); |
} |
RETURN_IF_ERROR(RenderField(field, map_key, ow)); |
+ } else { |
+ // The Type info for this map entry is incorrect. It should contain |
+ // exactly two fields with field number 1 and 2. |
+ return Status(util::error::INTERNAL, "Invalid map entry."); |
} |
} |
stream_->PopLimit(old_limit); |
@@ -266,7 +313,7 @@ Status ProtoStreamObjectSource::RenderTimestamp( |
pair<int64, int32> p = os->ReadSecondsAndNanos(type); |
int64 seconds = p.first; |
int32 nanos = p.second; |
- if (seconds > kMaxSeconds || seconds < kMinSeconds) { |
+ if (seconds > kTimestampMaxSeconds || seconds < kTimestampMinSeconds) { |
return Status( |
util::error::INTERNAL, |
StrCat("Timestamp seconds exceeds limit for field: ", field_name)); |
@@ -290,7 +337,7 @@ Status ProtoStreamObjectSource::RenderDuration( |
pair<int64, int32> p = os->ReadSecondsAndNanos(type); |
int64 seconds = p.first; |
int32 nanos = p.second; |
- if (seconds > kMaxSeconds || seconds < kMinSeconds) { |
+ if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds) { |
return Status( |
util::error::INTERNAL, |
StrCat("Duration seconds exceeds limit for field: ", field_name)); |
@@ -701,7 +748,9 @@ Status ProtoStreamObjectSource::RenderField( |
if (use_type_renderer) { |
RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow)); |
} else { |
+ RETURN_IF_ERROR(IncrementRecursionDepth(type->name(), field_name)); |
RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow)); |
+ --recursion_depth_; |
} |
if (!stream_->ConsumedEntireMessage()) { |
return Status(util::error::INVALID_ARGUMENT, |
@@ -807,7 +856,10 @@ Status ProtoStreamObjectSource::RenderNonMessageField( |
const google::protobuf::EnumValue* enum_value = |
FindEnumValueByNumber(*en, buffer32); |
if (enum_value != NULL) { |
- ow->RenderString(field_name, enum_value->name()); |
+ if (use_lower_camel_for_enums_) |
+ ow->RenderString(field_name, ToCamelCase(enum_value->name())); |
+ else |
+ ow->RenderString(field_name, enum_value->name()); |
} |
} else { |
GOOGLE_LOG(INFO) << "Unknown enum skipped: " << field->type_url(); |
@@ -994,6 +1046,17 @@ std::pair<int64, int32> ProtoStreamObjectSource::ReadSecondsAndNanos( |
return std::pair<int64, int32>(signed_seconds, signed_nanos); |
} |
+Status ProtoStreamObjectSource::IncrementRecursionDepth( |
+ StringPiece type_name, StringPiece field_name) const { |
+ if (++recursion_depth_ > max_recursion_depth_) { |
+ return Status( |
+ util::error::INVALID_ARGUMENT, |
+ StrCat("Message too deep. Max recursion depth reached for type '", |
+ type_name, "', field '", field_name, "'")); |
+ } |
+ return Status::OK; |
+} |
+ |
namespace { |
// TODO(skarvaje): Speed this up by not doing a linear scan. |
const google::protobuf::Field* FindFieldByNumber( |