CVE-2022-3509 (published 2022-12-12T13:15Z)

A parsing issue similar to CVE-2022-3171, but with textformat in protobuf-java core and lite versions prior to 3.21.7, 3.20.3, 3.19.6 and 3.16.3 can lead to a denial of service attack. Inputs containing multiple instances of non-repeated embedded messages with repeated or unknown fields causes objects to be converted back-n-forth between mutable and immutable forms, resulting in potentially long garbage collection pauses. We recommend updating to the versions mentioned above.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/protocolbuffers/protobuf
commit_details: repo_url: https://github.com/protocolbuffers/protobuf
author: Mike Kruskal
author_date: 2022-09-29 10:09:39-07:00
msg: Clean up TextFormat parser (#10673)

* Fix TextFormat parser
repository: repo_url: https://github.com/protocolbuffers/protobuf
repo_name: protocolbuffers/protobuf
description: Protocol Buffers - Google's data interchange format
repo_language: C++
owner: protocolbuffers
file_changes: filename: CHANGES.txt
change_type: ModificationType.MODIFY
diff: @@ -7,7 +7,11 @@
   * Change the Lite runtime to prefer merging from the wireformat into mutable
     messages rather than building up a new immutable object before merging. This
     way results in fewer allocations and copy operations.
-  * Make message-type extensions merge from wire-format instead of building up instances and merging afterwards. This has much better performance.
+  * Make message-type extensions merge from wire-format instead of building up
+    instances and merging afterwards. This has much better performance.
+  * Fix TextFormat parser to build up recurring (but supposedly not repeated)
+    sub-messages directly from text rather than building a new sub-message and
+    merging the fully formed message into the existing field.
 
 2022-09-13 version 21.6 (C++/Java/Python/PHP/Objective-C/C#/Ruby)
 

programming_language: Markdown
filename: MessageReflection.java
change_type: ModificationType.MODIFY
diff: @@ -378,6 +378,7 @@ class MessageReflection {
   static class BuilderAdapter implements MergeTarget {
 
     private final Message.Builder builder;
+    private boolean hasNestedBuilders = true;
 
     @Override
     public Descriptors.Descriptor getDescriptorForType() {
@@ -393,6 +394,17 @@ class MessageReflection {
       return builder.getField(field);
     }
 
+    private Message.Builder getFieldBuilder(Descriptors.FieldDescriptor field) {
+      if (hasNestedBuilders) {
+        try {
+          return builder.getFieldBuilder(field);
+        } catch (UnsupportedOperationException e) {
+          hasNestedBuilders = false;
+        }
+      }
+      return null;
+    }
+
     @Override
     public boolean hasField(Descriptors.FieldDescriptor field) {
       return builder.hasField(field);
@@ -400,6 +412,12 @@ class MessageReflection {
 
     @Override
     public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) {
+      if (!field.isRepeated() && value instanceof MessageLite.Builder) {
+        if (value != getFieldBuilder(field)) {
+          builder.setField(field, ((MessageLite.Builder) value).buildPartial());
+        }
+        return this;
+      }
       builder.setField(field, value);
       return this;
     }
@@ -413,12 +431,18 @@ class MessageReflection {
     @Override
     public MergeTarget setRepeatedField(
         Descriptors.FieldDescriptor field, int index, Object value) {
+      if (value instanceof MessageLite.Builder) {
+        value = ((MessageLite.Builder) value).buildPartial();
+      }
       builder.setRepeatedField(field, index, value);
       return this;
     }
 
     @Override
     public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) {
+      if (value instanceof MessageLite.Builder) {
+        value = ((MessageLite.Builder) value).buildPartial();
+      }
       builder.addRepeatedField(field, value);
       return this;
     }
@@ -536,11 +560,19 @@ class MessageReflection {
         Message defaultInstance)
         throws IOException {
       if (!field.isRepeated()) {
+        Message.Builder subBuilder;
         if (hasField(field)) {
-          input.readGroup(field.getNumber(), builder.getFieldBuilder(field), extensionRegistry);
-          return;
+          subBuilder = getFieldBuilder(field);
+          if (subBuilder != null) {
+            input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
+            return;
+          } else {
+            subBuilder = newMessageFieldInstance(field, defaultInstance);
+            subBuilder.mergeFrom((Message) getField(field));
+          }
+        } else {
+          subBuilder = newMessageFieldInstance(field, defaultInstance);
         }
-        Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
         input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
         Object unused = setField(field, subBuilder.buildPartial());
       } else {
@@ -558,11 +590,19 @@ class MessageReflection {
         Message defaultInstance)
         throws IOException {
       if (!field.isRepeated()) {
+        Message.Builder subBuilder;
         if (hasField(field)) {
-          input.readMessage(builder.getFieldBuilder(field), extensionRegistry);
-          return;
+          subBuilder = getFieldBuilder(field);
+          if (subBuilder != null) {
+            input.readMessage(subBuilder, extensionRegistry);
+            return;
+          } else {
+            subBuilder = newMessageFieldInstance(field, defaultInstance);
+            subBuilder.mergeFrom((Message) getField(field));
+          }
+        } else {
+          subBuilder = newMessageFieldInstance(field, defaultInstance);
         }
-        Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
         input.readMessage(subBuilder, extensionRegistry);
         Object unused = setField(field, subBuilder.buildPartial());
       } else {
@@ -586,11 +626,14 @@ class MessageReflection {
     public MergeTarget newMergeTargetForField(
         Descriptors.FieldDescriptor field, Message defaultInstance) {
       Message.Builder subBuilder;
-      if (defaultInstance != null) {
-        subBuilder = defaultInstance.newBuilderForType();
-      } else {
-        subBuilder = builder.newBuilderForField(field);
+      if (!field.isRepeated() && hasField(field)) {
+        subBuilder = getFieldBuilder(field);
+        if (subBuilder != null) {
+          return new BuilderAdapter(subBuilder);
+        }
       }
+
+      subBuilder = newMessageFieldInstance(field, defaultInstance);
       if (!field.isRepeated()) {
         Message originalMessage = (Message) getField(field);
         if (originalMessage != null) {
@@ -626,7 +669,7 @@ class MessageReflection {
 
     @Override
     public Object finish() {
-      return builder.buildPartial();
+      return builder;
     }
   }
 

programming_language: Java