diff options
Diffstat (limited to 'tests/src')
29 files changed, 751 insertions, 45 deletions
diff --git a/tests/src/test/cc/complex_proto_fuzzer.cc b/tests/src/test/cc/complex_proto_fuzzer.cc new file mode 100644 index 00000000..b9eea8bb --- /dev/null +++ b/tests/src/test/cc/complex_proto_fuzzer.cc @@ -0,0 +1,22 @@ +// Copyright 2023 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. + +#include "src/libfuzzer/libfuzzer_macro.h" +#include "src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/proto2.pb.h" + +DEFINE_PROTO_FUZZER(const com::code_intelligence::jazzer::protobuf::TestProtobuf& proto) { + if (proto.i32() == 1234 && proto.str() == "abcd") { + abort(); + } +} diff --git a/tests/src/test/data/crash_resistant_coverage_test/crashing_seeds/crash b/tests/src/test/data/crash_resistant_coverage_test/crashing_seeds/crash new file mode 100644 index 00000000..7c4a013e --- /dev/null +++ b/tests/src/test/data/crash_resistant_coverage_test/crashing_seeds/crash @@ -0,0 +1 @@ +aaa
\ No newline at end of file diff --git a/tests/src/test/data/crash_resistant_coverage_test/crashing_seeds/empty_input b/tests/src/test/data/crash_resistant_coverage_test/crashing_seeds/empty_input new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/src/test/data/crash_resistant_coverage_test/crashing_seeds/empty_input diff --git a/tests/src/test/data/crash_resistant_coverage_test/new_coverage_seeds/new_coverage b/tests/src/test/data/crash_resistant_coverage_test/new_coverage_seeds/new_coverage new file mode 100644 index 00000000..51497b75 --- /dev/null +++ b/tests/src/test/data/crash_resistant_coverage_test/new_coverage_seeds/new_coverage @@ -0,0 +1 @@ +aaaaaaaaaaaaaaaaa
\ No newline at end of file diff --git a/tests/src/test/java/com/example/AutofuzzAssertionErrorTarget.java b/tests/src/test/java/com/example/AutofuzzAssertionErrorTarget.java new file mode 100644 index 00000000..d692371f --- /dev/null +++ b/tests/src/test/java/com/example/AutofuzzAssertionErrorTarget.java @@ -0,0 +1,23 @@ +/* + * Copyright 2023 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.example; + +public class AutofuzzAssertionErrorTarget { + public static void autofuzz(byte[] b) { + assert b == null || b.length <= 5 || b[3] != 7; + } +} diff --git a/tests/src/test/java/com/example/AutofuzzCrashingSetterTarget.java b/tests/src/test/java/com/example/AutofuzzCrashingSetterTarget.java new file mode 100644 index 00000000..1af0c7bf --- /dev/null +++ b/tests/src/test/java/com/example/AutofuzzCrashingSetterTarget.java @@ -0,0 +1,21 @@ +/* + * Copyright 2023 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.example; + +public class AutofuzzCrashingSetterTarget extends Thread { + public void start(final byte[] out) {} +} diff --git a/tests/src/test/java/com/example/AutofuzzIgnoreTarget.java b/tests/src/test/java/com/example/AutofuzzIgnoreTarget.java new file mode 100644 index 00000000..d71ca4d9 --- /dev/null +++ b/tests/src/test/java/com/example/AutofuzzIgnoreTarget.java @@ -0,0 +1,28 @@ +// Copyright 2022 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.example; + +public class AutofuzzIgnoreTarget { + @SuppressWarnings("unused") + public void doStuff(String data) { + if (data.isEmpty()) { + throw new NullPointerException(); + } + if (data.length() < 10) { + throw new IllegalArgumentException(); + } + throw new RuntimeException(); + } +} diff --git a/tests/src/test/java/com/example/CoverageFuzzer.java b/tests/src/test/java/com/example/CoverageFuzzer.java index 8f63639d..1d65d3b7 100644 --- a/tests/src/test/java/com/example/CoverageFuzzer.java +++ b/tests/src/test/java/com/example/CoverageFuzzer.java @@ -18,10 +18,6 @@ package com.example; import com.code_intelligence.jazzer.api.FuzzedDataProvider; import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; -import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.ExecutionData; -import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.ExecutionDataReader; -import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.ExecutionDataStore; -import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.SessionInfoStore; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; @@ -30,6 +26,10 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.ExecutionDataReader; +import org.jacoco.core.data.ExecutionDataStore; +import org.jacoco.core.data.SessionInfoStore; /** * Test of coverage report and dump. @@ -171,7 +171,7 @@ public final class CoverageFuzzer { assertEquals(7, countHits(coverageFuzzerCoverage.getProbes())); assertEquals("com/example/CoverageFuzzer$ClassToCover", classToCoverCoverage.getName()); - assertEquals(11, countHits(classToCoverCoverage.getProbes())); + assertEquals(10, countHits(classToCoverCoverage.getProbes())); } private static int countHits(boolean[] probes) { diff --git a/tests/src/test/java/com/example/CrashResistantCoverageTarget.java b/tests/src/test/java/com/example/CrashResistantCoverageTarget.java new file mode 100644 index 00000000..c88d4509 --- /dev/null +++ b/tests/src/test/java/com/example/CrashResistantCoverageTarget.java @@ -0,0 +1,37 @@ +// Copyright 2022 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.example; + +import java.time.Instant; + +public class CrashResistantCoverageTarget { + public static void fuzzerTestOneInput(byte[] data) { + if (data.length < 10) { + // Crash immediately on the empty and the first seed input so that we can verify that the + // crash-resistant merge strategy actually works. + throw new IllegalStateException("Crash"); + } + if (data.length < 100) { + someFunction(); + } + } + + public static void someFunction() { + // A non-trivial condition that always evaluates to true. + if (Instant.now().getNano() >= 0) { + System.out.println("Hello, world!"); + } + } +} diff --git a/tests/src/test/java/com/example/DisabledHooksFuzzer.java b/tests/src/test/java/com/example/DisabledHooksFuzzer.java index 430bfa40..f9dbdcba 100644 --- a/tests/src/test/java/com/example/DisabledHooksFuzzer.java +++ b/tests/src/test/java/com/example/DisabledHooksFuzzer.java @@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandle; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +@SuppressWarnings("InvalidPatternSyntax") public class DisabledHooksFuzzer { public static void fuzzerTestOneInput(byte[] data) { triggerCustomHook(); diff --git a/tests/src/test/java/com/example/ExperimentalMutatorComplexProtoFuzzer.java b/tests/src/test/java/com/example/ExperimentalMutatorComplexProtoFuzzer.java new file mode 100644 index 00000000..4c3ed31b --- /dev/null +++ b/tests/src/test/java/com/example/ExperimentalMutatorComplexProtoFuzzer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 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.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.mutation.annotation.InRange; +import com.code_intelligence.jazzer.mutation.annotation.NotNull; +import com.code_intelligence.jazzer.protobuf.Proto2.TestProtobuf; + +public class ExperimentalMutatorComplexProtoFuzzer { + public static void fuzzerTestOneInput(@NotNull TestProtobuf proto) { + if (proto.getI32() == 1234 && proto.getStr().equals("abcd")) { + throw new FuzzerSecurityIssueMedium("Secret proto is found!"); + } + } +} diff --git a/tests/src/test/java/com/example/ExperimentalMutatorDynamicProtoFuzzer.java b/tests/src/test/java/com/example/ExperimentalMutatorDynamicProtoFuzzer.java new file mode 100644 index 00000000..bbca1ddc --- /dev/null +++ b/tests/src/test/java/com/example/ExperimentalMutatorDynamicProtoFuzzer.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 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.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.mutation.annotation.NotNull; +import com.code_intelligence.jazzer.mutation.annotation.proto.WithDefaultInstance; +import com.google.protobuf.DescriptorProtos.DescriptorProto; +import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; +import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Type; +import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.DescriptorValidationException; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.DynamicMessage; +import com.google.protobuf.Message; + +public class ExperimentalMutatorDynamicProtoFuzzer { + public static void fuzzerTestOneInput(@NotNull @WithDefaultInstance( + "com.example.ExperimentalMutatorDynamicProtoFuzzer#getDefaultInstance") Message proto) { + FieldDescriptor I32 = proto.getDescriptorForType().findFieldByName("i32"); + FieldDescriptor STR = proto.getDescriptorForType().findFieldByName("str"); + if (proto.getField(I32).equals(1234) && proto.getField(STR).equals("abcd")) { + throw new FuzzerSecurityIssueMedium("Secret proto is found!"); + } + } + + @SuppressWarnings("unused") + private static DynamicMessage getDefaultInstance() { + DescriptorProto myMessage = + DescriptorProto.newBuilder() + .setName("my_message") + .addField(FieldDescriptorProto.newBuilder().setNumber(1).setName("i32").setType( + Type.TYPE_INT32)) + .addField(FieldDescriptorProto.newBuilder().setNumber(2).setName("str").setType( + Type.TYPE_STRING)) + .build(); + FileDescriptorProto file = FileDescriptorProto.newBuilder() + .setName("my_protos.proto") + .addMessageType(myMessage) + .build(); + try { + return DynamicMessage.getDefaultInstance(FileDescriptor.buildFrom(file, new FileDescriptor[0]) + .findMessageTypeByName("my_message")); + } catch (DescriptorValidationException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/tests/src/test/java/com/example/ExperimentalMutatorFuzzer.java b/tests/src/test/java/com/example/ExperimentalMutatorFuzzer.java new file mode 100644 index 00000000..9645e817 --- /dev/null +++ b/tests/src/test/java/com/example/ExperimentalMutatorFuzzer.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 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.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.mutation.annotation.InRange; +import com.code_intelligence.jazzer.mutation.annotation.NotNull; + +public class ExperimentalMutatorFuzzer { + public static void fuzzerTestOneInput( + @InRange(max = -42) short num, @NotNull SimpleProto.MyProto proto) { + if (num > -42) { + throw new IllegalArgumentException(); + } + + if (proto.getNumber() == 12345678) { + if (proto.getMessage().getText().contains("Hello, proto!")) { + throw new FuzzerSecurityIssueMedium("Dangerous proto"); + } + } + } +} diff --git a/tests/src/test/java/com/example/HookDependenciesFuzzer.java b/tests/src/test/java/com/example/HookDependenciesFuzzer.java index 88627f4c..7150ed6c 100644 --- a/tests/src/test/java/com/example/HookDependenciesFuzzer.java +++ b/tests/src/test/java/com/example/HookDependenciesFuzzer.java @@ -26,29 +26,6 @@ import java.util.regex.Pattern; // 2. hooks that are not shipped in the Jazzer agent JAR can still instrument Java standard library // classes. public class HookDependenciesFuzzer { - private static final Field PATTERN_ROOT; - - static { - Field root; - try { - root = Pattern.class.getDeclaredField("root"); - } catch (NoSuchFieldException e) { - root = null; - } - PATTERN_ROOT = root; - } - - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Matcher", - targetMethod = "matches", targetMethodDescriptor = "()Z", - additionalClassesToHook = {"java.util.regex.Pattern"}) - public static void - matcherMatchesHook(MethodHandle method, Object alwaysNull, Object[] alwaysEmpty, int hookId, - Boolean returnValue) { - if (PATTERN_ROOT != null) { - throw new FuzzerSecurityIssueLow("Hook applied even though it depends on the class to hook"); - } - } - public static void fuzzerTestOneInput(byte[] data) { try { Pattern.matches("foobar", "foobar"); diff --git a/tests/src/test/java/com/example/HookDependenciesFuzzerHooks.java b/tests/src/test/java/com/example/HookDependenciesFuzzerHooks.java new file mode 100644 index 00000000..d4f50dbf --- /dev/null +++ b/tests/src/test/java/com/example/HookDependenciesFuzzerHooks.java @@ -0,0 +1,47 @@ +// Copyright 2022 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.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; +import com.code_intelligence.jazzer.api.HookType; +import com.code_intelligence.jazzer.api.MethodHook; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import java.util.regex.Pattern; + +public class HookDependenciesFuzzerHooks { + private static final Field PATTERN_ROOT; + + static { + Field root; + try { + root = Pattern.class.getDeclaredField("root"); + } catch (NoSuchFieldException e) { + root = null; + } + PATTERN_ROOT = root; + } + + @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Matcher", + targetMethod = "matches", targetMethodDescriptor = "()Z", + additionalClassesToHook = {"java.util.regex.Pattern"}) + public static void + matcherMatchesHook(MethodHandle method, Object alwaysNull, Object[] alwaysEmpty, int hookId, + Boolean returnValue) { + if (PATTERN_ROOT != null) { + throw new FuzzerSecurityIssueLow("Hook applied even though it depends on the class to hook"); + } + } +} diff --git a/tests/src/test/java/com/example/JUnitAgentConfigurationFuzzTest.java b/tests/src/test/java/com/example/JUnitAgentConfigurationFuzzTest.java new file mode 100644 index 00000000..4f8c2a19 --- /dev/null +++ b/tests/src/test/java/com/example/JUnitAgentConfigurationFuzzTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023 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.example; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.code_intelligence.jazzer.junit.FuzzTest; +import java.util.function.Supplier; + +class JUnitAgentConfigurationFuzzTest { + @FuzzTest + void testConfiguration(byte[] bytes) { + assertEquals(singletonList("com.example.**"), getLazyOptValue("instrumentationIncludes")); + assertEquals(singletonList("com.example.**"), getLazyOptValue("customHookIncludes")); + } + + private static Object getLazyOptValue(String name) { + try { + Supplier<Object> supplier = + (Supplier<Object>) Class.forName("com.code_intelligence.jazzer.driver.Opt") + .getField(name) + .get(null); + return supplier.get(); + } catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/tests/src/test/java/com/example/JUnitAssertFuzzer.java b/tests/src/test/java/com/example/JUnitAssertFuzzer.java new file mode 100644 index 00000000..d2644281 --- /dev/null +++ b/tests/src/test/java/com/example/JUnitAssertFuzzer.java @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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.example; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +public class JUnitAssertFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + assertNotEquals("JUnit rocks!", data.consumeRemainingAsString()); + } +} diff --git a/tests/src/test/java/com/example/KotlinVararg.kt b/tests/src/test/java/com/example/KotlinVararg.kt new file mode 100644 index 00000000..81974eba --- /dev/null +++ b/tests/src/test/java/com/example/KotlinVararg.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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.example + +class KotlinVararg(vararg opts: String) { + private val allOpts = opts.toList().joinToString(", ") + + fun doStuff() = allOpts +} diff --git a/tests/src/test/java/com/example/KotlinVarargFuzzer.java b/tests/src/test/java/com/example/KotlinVarargFuzzer.java new file mode 100644 index 00000000..3324e2e8 --- /dev/null +++ b/tests/src/test/java/com/example/KotlinVarargFuzzer.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.io.IOException; + +public class KotlinVarargFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) throws IOException { + String out = new KotlinVararg(data.consumeRemainingAsString().split("; ")).doStuff(); + if (out.contains("a, a")) { + throw new IOException(out); + } + } +} diff --git a/tests/src/test/java/com/example/OfflineInstrumentedFuzzer.java b/tests/src/test/java/com/example/OfflineInstrumentedFuzzer.java new file mode 100644 index 00000000..eb7da480 --- /dev/null +++ b/tests/src/test/java/com/example/OfflineInstrumentedFuzzer.java @@ -0,0 +1,23 @@ +/* + * Copyright 2023 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.example; + +public class OfflineInstrumentedFuzzer { + public static void fuzzerTestOneInput(byte[] data) { + OfflineInstrumentedTarget.someFunction(data); + } +} diff --git a/tests/src/test/java/com/example/OfflineInstrumentedTarget.java b/tests/src/test/java/com/example/OfflineInstrumentedTarget.java new file mode 100644 index 00000000..52347270 --- /dev/null +++ b/tests/src/test/java/com/example/OfflineInstrumentedTarget.java @@ -0,0 +1,25 @@ +/* + * Copyright 2023 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.example; + +public class OfflineInstrumentedTarget { + public static void someFunction(byte[] data) { + if (new String(data).equals("found it")) { + throw new IllegalStateException("Expected exception"); + } + } +} diff --git a/tests/src/test/java/com/example/SilencedFuzzer.java b/tests/src/test/java/com/example/SilencedFuzzer.java new file mode 100644 index 00000000..d1d8777a --- /dev/null +++ b/tests/src/test/java/com/example/SilencedFuzzer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2023 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.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh; +import java.io.OutputStream; +import java.io.PrintStream; + +public class SilencedFuzzer { + private static final PrintStream noopStream = new PrintStream(new OutputStream() { + @Override + public void write(int b) {} + }); + + public static void fuzzerInitialize() { + System.setErr(noopStream); + System.setOut(noopStream); + } + + public static void fuzzerTestOneInput(byte[] input) { + // If the FuzzTargetTestWrapper successfully parses the stack trace emitted by this finding, we + // know that the fuzzer still emitted output despite the fact that System.err and System.out + // have been redirected above. + throw new FuzzerSecurityIssueHigh(); + } +} diff --git a/tests/src/test/java/com/example/TimeoutFuzzer.java b/tests/src/test/java/com/example/TimeoutFuzzer.java new file mode 100644 index 00000000..952113bf --- /dev/null +++ b/tests/src/test/java/com/example/TimeoutFuzzer.java @@ -0,0 +1,24 @@ +/* + * Copyright 2022 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.example; + +public class TimeoutFuzzer { + public static void fuzzerTestOneInput(byte[] b) { + while (true) { + } + } +} diff --git a/tests/src/test/native/com/example/BUILD.bazel b/tests/src/test/native/com/example/BUILD.bazel index 93b886a8..80657364 100644 --- a/tests/src/test/native/com/example/BUILD.bazel +++ b/tests/src/test/native/com/example/BUILD.bazel @@ -1,28 +1,16 @@ load("@fmeum_rules_jni//jni:defs.bzl", "cc_jni_library") +load("//bazel:compat.bzl", "SKIP_ON_WINDOWS") cc_jni_library( name = "native_value_profile_fuzzer", srcs = ["native_value_profile_fuzzer.cpp"], copts = [ - "-fsanitize=fuzzer-no-link,address", - "-fno-sanitize-blacklist", + "-fsanitize=fuzzer-no-link", ], - defines = [ - # Workaround for Windows build failures with VS 2022: - # "lld-link: error: /INFERASANLIBS is not allowed in .drectve" - # https://github.com/llvm/llvm-project/issues/56300#issuecomment-1214313292 - "_DISABLE_STRING_ANNOTATION=1", - "_DISABLE_VECTOR_ANNOTATION=1", + linkopts = [ + "-fsanitize=fuzzer-no-link", ], - linkopts = select({ - "//:clang_on_linux": ["-fuse-ld=lld"], - "@platforms//os:windows": [ - # Windows requires all symbols that should be imported from the main - # executable to be defined by an import lib. - "/wholearchive:clang_rt.asan_dll_thunk-x86_64.lib", - ], - "//conditions:default": [], - }), + target_compatible_with = SKIP_ON_WINDOWS, visibility = ["//tests:__pkg__"], deps = ["//tests:native_value_profile_fuzzer.hdrs"], ) diff --git a/tests/src/test/proto/BUILD.bazel b/tests/src/test/proto/BUILD.bazel new file mode 100644 index 00000000..7f34e6ab --- /dev/null +++ b/tests/src/test/proto/BUILD.bazel @@ -0,0 +1,10 @@ +proto_library( + name = "simple_proto", + srcs = ["simple_proto.proto"], +) + +java_proto_library( + name = "simple_java_proto", + visibility = ["//tests:__pkg__"], + deps = [":simple_proto"], +) diff --git a/tests/src/test/proto/simple_proto.proto b/tests/src/test/proto/simple_proto.proto new file mode 100644 index 00000000..b8c10e37 --- /dev/null +++ b/tests/src/test/proto/simple_proto.proto @@ -0,0 +1,29 @@ +// Copyright 2023 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. + +syntax = "proto3"; + +package com.example; + +option java_package = "com.example"; + +message MyProto { + uint64 number = 1; + MySubProto message = 2; +} + +message MySubProto { + string text = 1; +} + diff --git a/tests/src/test/shell/crash_resistant_coverage_test.sh b/tests/src/test/shell/crash_resistant_coverage_test.sh new file mode 100755 index 00000000..f7fe281c --- /dev/null +++ b/tests/src/test/shell/crash_resistant_coverage_test.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# Copyright 2022 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. + +# This test verifies that Jazzer's --nohook mode can be used to measure code coverage using the +# JaCoCo agent. +# It loosely follows the OSS-Fuzz merge logic, which is the most important user of this feature: +# https://github.com/google/oss-fuzz/blob/b8ef6a216dc592f4f491daa35c815b14260315c0/infra/base-images/base-runner/coverage#L181 +# The use of libFuzzer's -merge feature should allow coverage collection to proceed through crashing +# inputs, which is also verified by this test. + +# --- begin runfiles.bash initialization v2 --- +# Copy-pasted from the Bazel Bash runfiles library v2. +set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e +# --- end runfiles.bash initialization v2 --- + +function fail() { + echo "FAILED: $1" + exit 1 +} + + +class_dump_dir=$TEST_TMPDIR/classes +mkdir -p "$class_dump_dir" +exec_file=$TEST_TMPDIR/jacoco.exec +excludes='com.code_intelligence.jazzer.**\:com.sun.tools.attach.**\:sun.tools.attach.**\:sun.jvmstat.**' +jacoco_args="destfile=$exec_file,classdumpdir=$class_dump_dir,excludes=$excludes" + +corpus_dummy=$TEST_TMPDIR/corpus +mkdir -p "$corpus_dummy" +"$(rlocation jazzer/launcher/jazzer)" \ + --cp="$(rlocation jazzer/tests/CrashResistantCoverageTarget_deploy.jar)" \ + --target_class=com.example.CrashResistantCoverageTarget \ + -merge=1 -timeout=100 --nohooks \ + "--additional_jvm_args=-javaagent\\:$(rlocation jacocoagent/file/jacocoagent.jar)=${jacoco_args}" \ + "$corpus_dummy" \ + "$(rlocation jazzer/tests/src/test/data/crash_resistant_coverage_test/crashing_seeds)" \ + "$(rlocation jazzer/tests/src/test/data/crash_resistant_coverage_test/new_coverage_seeds)" + +[[ -e $exec_file ]] || fail "JaCoCo .exec file does not exist" +[[ -s $exec_file ]] || fail "JaCoCo .exec file is empty" + +# Available under bazel-testlogs/tests/crash_resistant_coverage_test/test.outputs after the test. +xml_report=$TEST_UNDECLARED_OUTPUTS_DIR/report.xml +java -jar "$(rlocation jacococli/file/jacococli.jar)" report "$exec_file" \ + --xml "$xml_report" \ + --classfiles "$class_dump_dir" + +# Verify that no unexpected class is contained in the report. +grep -o -P '<class name="(?!com\/example\/CrashResistantCoverageTarget)[^"]*"' "$xml_report" && fail "Unexpected class contained in coverage report" + +# Verify that fuzzerTestOneInput and someFunction are fully covered by matching the opening <method> +# tag and a child <counter> tag - (?:[^<]|<[^\/]).* matches everything but </, so there can't be a +# </method> between the two. +# Similarly, verify that <init> isn't covered as the default constructor is never invoked. +grep -q -P '\Q<method name="<init>" desc="()V" line="19">\E(?:[^<]|<[^\/])*\Q<counter type="LINE" missed="1" covered="0"/>\E' "$xml_report" && fail "<init> has been covered" +grep -q -P '\Q<method name="fuzzerTestOneInput" desc="([B)V" line="21">\E(?:[^<]|<[^\/])*\Q<counter type="LINE" missed="0" covered="5"/>\E' "$xml_report" || fail "fuzzerTestOneInput hasn't been covered" +grep -q -P '\Q<method name="someFunction" desc="()V" line="33">\E(?:[^<]|<[^\/])*\Q<counter type="LINE" missed="0" covered="3"/>\E' "$xml_report" || fail "someFunction hasn't been covered" diff --git a/tests/src/test/shell/jazzer_from_path_test.sh b/tests/src/test/shell/jazzer_from_path_test.sh new file mode 100755 index 00000000..357fde68 --- /dev/null +++ b/tests/src/test/shell/jazzer_from_path_test.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright 2022 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. + +# Verify that the Jazzer launcher finds the jar when executed from PATH. + + +# --- begin runfiles.bash initialization v3 --- +# Copy-pasted from the Bazel Bash runfiles library v3. +set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e +# --- end runfiles.bash initialization v3 --- + +# Unpack the release archive to a temporary directory. +jazzer_release="$(rlocation "$1")" +tmp="$(mktemp -d)" +trap 'rm -r "$tmp"' EXIT +# GNU tar on Windows requires --force-local to support colons in archives names, +# macOS tar does not support it. +tar -xzf "$jazzer_release" -C "$tmp" --force-local || tar -xzf "$jazzer_release" -C "$tmp" + +# Add the Jazzer launcher to PATH first so that it is picked over host Jazzer +# installations. +PATH="$tmp:$PATH" +export PATH + +jazzer --version diff --git a/tests/src/test/shell/junit_agent_configuration_test.sh b/tests/src/test/shell/junit_agent_configuration_test.sh new file mode 100755 index 00000000..dd029825 --- /dev/null +++ b/tests/src/test/shell/junit_agent_configuration_test.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright 2022 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. + +# Verify that instrumentation filter defaults set by @FuzzTest work. + +# --- begin runfiles.bash initialization v2 --- +# Copy-pasted from the Bazel Bash runfiles library v2. +set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e +# --- end runfiles.bash initialization v2 --- + +function fail() { + echo "FAILED: $1" + exit 1 +} + +stderr="$TEST_TMPDIR/stderr" + +"$(rlocation "$1")" --target_class=com.example.JUnitAgentConfigurationFuzzTest 2>&1 -runs=1 | tee "$stderr" || fail "Jazzer did not exit with exit code 0" + +[[ $(grep -c "INFO: Instrumented " "$stderr") == 1 ]] || fail "Expected exactly one instrumented class" +[[ $(grep "INFO: Instrumented " "$stderr" | grep -c -v "INFO: Instrumented com.example.") == 0 ]] || fail "Expected all instrumented classes to be in com.example" |