aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Meumertzheim <meumertzheim@code-intelligence.com>2023-06-06 08:42:07 +0200
committerFabian Meumertzheim <fabian@meumertzhe.im>2023-06-06 09:35:06 +0200
commit3869b7395de165e94093379e83adf6e978871cb7 (patch)
tree49ac4207fb8cfcb03e868538a64bfc3d9df1efb3
parentc29e73cd486ecff0f2c381747bd7e1b0d6ffc7cd (diff)
downloadjazzer-api-3869b7395de165e94093379e83adf6e978871cb7.tar.gz
mutator: Set required fields to defaults when reading incomplete proto
A proto message deserialized from bytes can be incomplete, which results in an error when building it if required fields are missing. To prevent this, walk the proto and initialize all unset required fields to defaults recursively.
-rw-r--r--src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java19
-rw-r--r--src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java33
-rw-r--r--src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/proto2.proto20
3 files changed, 72 insertions, 0 deletions
diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java
index 338942a0..f11acf13 100644
--- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java
+++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java
@@ -245,9 +245,28 @@ public final class BuilderMutatorFactory extends MutatorFactory {
// We never want the fuzz test to see unknown fields and our mutations should never produce
// them.
builder.setUnknownFields(UnknownFieldSet.getDefaultInstance());
+ // Required fields may not have been set at this point. We set them to default values to
+ // prevent an exception when built.
+ forceInitialized(builder);
return builder;
}
+ private void forceInitialized(Builder builder) {
+ if (builder.isInitialized()) {
+ return;
+ }
+ for (FieldDescriptor field : builder.getDescriptorForType().getFields()) {
+ if (!field.isRequired()) {
+ continue;
+ }
+ if (field.getJavaType() == JavaType.MESSAGE) {
+ forceInitialized(builder.getFieldBuilder(field));
+ } else if (!builder.hasField(field)) {
+ builder.setField(field, field.getDefaultValue());
+ }
+ }
+ }
+
@Override
public void write(Builder builder, DataOutputStream out) throws IOException {
Message message = builder.build();
diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java
index 5955b771..b804c7fb 100644
--- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java
+++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java
@@ -19,13 +19,22 @@ package com.code_intelligence.jazzer.mutation.mutator.proto;
import static com.code_intelligence.jazzer.mutation.support.TestSupport.mockPseudoRandom;
import static com.google.common.truth.Truth.assertThat;
+import com.code_intelligence.jazzer.mutation.annotation.NotNull;
import com.code_intelligence.jazzer.mutation.api.ChainedMutatorFactory;
import com.code_intelligence.jazzer.mutation.api.MutatorFactory;
import com.code_intelligence.jazzer.mutation.api.SerializingMutator;
import com.code_intelligence.jazzer.mutation.mutator.collection.CollectionMutators;
import com.code_intelligence.jazzer.mutation.mutator.lang.LangMutators;
import com.code_intelligence.jazzer.mutation.support.TestSupport.MockPseudoRandom;
+import com.code_intelligence.jazzer.mutation.support.TypeHolder;
+import com.code_intelligence.jazzer.protobuf.Proto2.ExtendedMessage2;
+import com.code_intelligence.jazzer.protobuf.Proto2.ExtendedSubmessage2;
+import com.code_intelligence.jazzer.protobuf.Proto2.OriginalMessage2;
+import com.code_intelligence.jazzer.protobuf.Proto2.OriginalSubmessage2;
import com.code_intelligence.jazzer.protobuf.Proto3.PrimitiveField3;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import org.junit.jupiter.api.Test;
class MessageMutatorTest {
@@ -56,4 +65,28 @@ class MessageMutatorTest {
assertThat(msg).isNotEqualTo(PrimitiveField3.getDefaultInstance());
}
}
+
+ @Test
+ void testIncompleteMessageWithRequiredFields() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ OriginalMessage2.newBuilder()
+ .setMessageField(OriginalSubmessage2.newBuilder().setNumericField(42).build())
+ .setBoolField(true)
+ .build()
+ .writeTo(out);
+ byte[] bytes = out.toByteArray();
+
+ SerializingMutator<ExtendedMessage2> mutator =
+ (SerializingMutator<ExtendedMessage2>) FACTORY.createOrThrow(
+ new TypeHolder<@NotNull ExtendedMessage2>() {}.annotatedType());
+ ExtendedMessage2 extendedMessage = mutator.readExclusive(new ByteArrayInputStream(bytes));
+ assertThat(extendedMessage)
+ .isEqualTo(ExtendedMessage2.newBuilder()
+ .setMessageField(
+ ExtendedSubmessage2.newBuilder().setNumericField(42).setMessageField(
+ OriginalSubmessage2.newBuilder().setNumericField(0).build()))
+ .setBoolField(true)
+ .setFloatField(0)
+ .build());
+ }
}
diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/proto2.proto b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/proto2.proto
index 77dbb5e8..ea7c9999 100644
--- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/proto2.proto
+++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/proto2.proto
@@ -139,3 +139,23 @@ message TestProtobuf {
message EmptyMessage {}
optional EmptyMessage empty_message = 101;
}
+
+message OriginalSubmessage2 {
+ required int32 numeric_field = 1;
+}
+
+message OriginalMessage2 {
+ required OriginalSubmessage2 message_field = 1;
+ required bool bool_field = 2;
+}
+
+message ExtendedSubmessage2 {
+ required int32 numeric_field = 1;
+ required OriginalSubmessage2 message_field = 2;
+}
+
+message ExtendedMessage2 {
+ required ExtendedSubmessage2 message_field = 1;
+ required bool bool_field = 2;
+ required float float_field = 3;
+}