From 9a804bb4272b35b10028a7dc246c6c63176e0eee Mon Sep 17 00:00:00 2001 From: Khaled Yakdan Date: Sun, 17 Oct 2021 21:49:09 +0200 Subject: Handle creating classes with a default constructor and setter methods --- .../jazzer/api/FuzzedDataProvider.java | 4 +- .../code_intelligence/jazzer/autofuzz/Meta.java | 46 ++++++++++++++---- .../code_intelligence/jazzer/autofuzz/BUILD.bazel | 15 ++++++ .../jazzer/autofuzz/SettersTest.java | 43 +++++++++++++++++ .../jazzer/autofuzz/testdata/BUILD.bazel | 5 ++ .../autofuzz/testdata/EmployeeWithSetters.java | 56 ++++++++++++++++++++++ 6 files changed, 159 insertions(+), 10 deletions(-) create mode 100644 agent/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java create mode 100644 agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/BUILD.bazel create mode 100644 agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java b/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java index 30ae526a..b1f38b50 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java +++ b/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java @@ -283,7 +283,7 @@ public interface FuzzedDataProvider { *

Note: The distribution of picks is not perfectly uniform. * * @param collection the {@link Collection} to pick an element from. - * @param the type of a collection element + * @param the type of the collection element * @return an element from {@code collection} chosen based on the fuzzer input */ @SuppressWarnings("unchecked") @@ -304,7 +304,7 @@ public interface FuzzedDataProvider { *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. - * @param the type of an array element + * @param the type of the array element * @return an element from {@code array} chosen based on the fuzzer input */ default T pickValue(T[] array) { diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java index 0d0c3190..fda9748b 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java +++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java @@ -18,6 +18,7 @@ import com.code_intelligence.jazzer.api.FuzzedDataProvider; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfoList; import io.github.classgraph.ScanResult; +import java.beans.PropertyDescriptor; import java.io.ByteArrayInputStream; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -25,10 +26,7 @@ import java.lang.reflect.Executable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.WeakHashMap; +import java.util.*; import java.util.stream.Collectors; import net.jodah.typetools.TypeResolver; import net.jodah.typetools.TypeResolver.Unknown; @@ -142,7 +140,19 @@ public class Meta { } return consume(data, data.pickValue(implementingClasses)); } else if (type.getConstructors().length > 0) { - return autofuzz(data, data.pickValue(type.getConstructors())); + Constructor constructor = data.pickValue(type.getConstructors()); + Object obj = autofuzz(data, constructor); + if (constructor.getParameterCount() == 0) { + List potentialSetters = getPotentialSetters(type); + if (!potentialSetters.isEmpty()) { + List pickedSetters = + data.pickValues(potentialSetters, data.consumeInt(0, potentialSetters.size())); + for (Method setter : pickedSetters) { + autofuzz(data, setter, obj); + } + } + } + return obj; } else if (getNestedBuilderClasses(type).size() > 0) { List> nestedBuilderClasses = getNestedBuilderClasses(type); Class pickedBuilder = data.pickValue(nestedBuilderClasses); @@ -160,16 +170,23 @@ public class Meta { Method builderMethod = data.pickValue(originalObjectCreationMethods); - Object obj = autofuzz(data, data.pickValue(pickedBuilder.getConstructors())); + Object builderObj = autofuzz(data, data.pickValue(pickedBuilder.getConstructors())); for (Method method : pickedMethods) { - obj = autofuzz(data, method, obj); + builderObj = autofuzz(data, method, builderObj); } try { - return builderMethod.invoke(obj); + return builderMethod.invoke(builderObj); } catch (Exception e) { throw new AutofuzzConstructionException(e); } + } else { + Constructor[] c = type.getDeclaredConstructors(); + System.err.printf("ctor: %s\n", c[0].toGenericString()); + System.err.printf(" public %b\n", Modifier.isPublic(c[0].getModifiers())); + System.err.printf(" private %b\n", Modifier.isPrivate(c[0].getModifiers())); + System.err.printf(" protected %b\n", Modifier.isProtected(c[0].getModifiers())); + // System.err.printf(" protected %b\n", Modifier.i(c[0].getModifiers())); } return null; } @@ -180,6 +197,19 @@ public class Meta { .collect(Collectors.toList()); } + private static List getPotentialSetters(Class type) { + List potentialSetters = new ArrayList<>(); + List methods = Arrays.asList(type.getMethods()); + methods.sort(Comparator.comparing(Method::getName)); + for (Method method : methods) { + if (void.class.equals(method.getReturnType()) && method.getParameterCount() == 1 + && method.getName().startsWith("set")) { + potentialSetters.add(method); + } + } + return potentialSetters; + } + private static Object[] consumeArguments(FuzzedDataProvider data, Executable executable) { Object[] result; try { diff --git a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel index 84d86e2a..cc71e09d 100644 --- a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel +++ b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel @@ -42,3 +42,18 @@ java_test( "@maven//:junit_junit", ], ) + +java_test( + name = "SettersTest", + size = "small", + srcs = [ + "SettersTest.java", + ], + test_class = "com.code_intelligence.jazzer.autofuzz.SettersTest", + deps = [ + "//agent/src/main/java/com/code_intelligence/jazzer/api", + "//agent/src/main/java/com/code_intelligence/jazzer/autofuzz", + "//agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata:test_data", + "@maven//:junit_junit", + ], +) diff --git a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java new file mode 100644 index 00000000..59a46636 --- /dev/null +++ b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java @@ -0,0 +1,43 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.code_intelligence.jazzer.autofuzz; + +import static org.junit.Assert.assertEquals; + +import com.code_intelligence.jazzer.api.CannedFuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.autofuzz.testdata.EmployeeWithSetters; +import java.util.Arrays; +import org.junit.Test; + +public class SettersTest { + FuzzedDataProvider data = + CannedFuzzedDataProvider.create(Arrays.asList(0, // pick first constructor + 2, // pick two setters + 1, // pick second setter + 0, // pick first setter + 6, // remaining bytes + "foo", // setFirstName + 26 // setAge + )); + + @Test + public void testEmptyConstructorWithSetters() { + EmployeeWithSetters employee = new EmployeeWithSetters(); + employee.setFirstName("foo"); + employee.setAge(26); + assertEquals(Meta.consume(data, EmployeeWithSetters.class), employee); + } +} diff --git a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/BUILD.bazel b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/BUILD.bazel new file mode 100644 index 00000000..c2c68803 --- /dev/null +++ b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/BUILD.bazel @@ -0,0 +1,5 @@ +java_library( + name = "test_data", + srcs = glob(["*.java"]), + visibility = ["//visibility:public"], +) diff --git a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java new file mode 100644 index 00000000..2c76a61f --- /dev/null +++ b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java @@ -0,0 +1,56 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.code_intelligence.jazzer.autofuzz.testdata; + +import java.util.Objects; + +public class EmployeeWithSetters { + private String firstName; + private String lastName; + private String jobTitle; + private int age; + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + EmployeeWithSetters hero = (EmployeeWithSetters) o; + return age == hero.age && Objects.equals(firstName, hero.firstName) + && Objects.equals(lastName, hero.lastName) && Objects.equals(jobTitle, hero.jobTitle); + } + + @Override + public int hashCode() { + return Objects.hash(firstName, lastName, jobTitle, age); + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public void setJobTitle(String jobTitle) { + this.jobTitle = jobTitle; + } + + public void setAge(int age) { + this.age = age; + } +} -- cgit v1.2.3