1 /* 2 * Copyright 2023 Code Intelligence GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.code_intelligence.jazzer.junit; 18 19 import com.code_intelligence.jazzer.api.FuzzedDataProvider; 20 import com.code_intelligence.jazzer.autofuzz.Meta; 21 import com.code_intelligence.jazzer.driver.FuzzedDataProviderImpl; 22 import com.code_intelligence.jazzer.driver.Opt; 23 import com.code_intelligence.jazzer.mutation.ArgumentsMutator; 24 import java.io.ByteArrayInputStream; 25 import java.io.ByteArrayOutputStream; 26 import java.lang.reflect.Method; 27 import java.util.Optional; 28 29 interface SeedSerializer { read(byte[] bytes)30 Object[] read(byte[] bytes); allReadsValid()31 default boolean allReadsValid() { 32 return true; 33 } 34 35 // Implementations can assume that the argument array contains valid arguments for the method that 36 // this instance has been constructed for. write(Object[] args)37 byte[] write(Object[] args) throws UnsupportedOperationException; 38 39 /** 40 * Creates specialized {@link SeedSerializer} instances for the following method parameters: 41 * <ul> 42 * <li>{@code byte[]} 43 * <li>{@code FuzzDataProvider} 44 * <li>Any other types will attempt to be created using either Autofuzz or the experimental 45 * mutator framework if {@link Opt}'s {@code experimentalMutator} is set. 46 * </ul> 47 */ of(Method method)48 static SeedSerializer of(Method method) { 49 if (method.getParameterCount() == 0) { 50 throw new IllegalArgumentException( 51 "Methods annotated with @FuzzTest must take at least one parameter"); 52 } 53 if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == byte[].class) { 54 return new ByteArraySeedSerializer(); 55 } else if (method.getParameterCount() == 1 56 && method.getParameterTypes()[0] == FuzzedDataProvider.class) { 57 return new FuzzedDataProviderSeedSerializer(); 58 } else { 59 Optional<ArgumentsMutator> argumentsMutator = 60 Opt.experimentalMutator ? ArgumentsMutator.forMethod(method) : Optional.empty(); 61 return argumentsMutator.<SeedSerializer>map(ArgumentsMutatorSeedSerializer::new) 62 .orElseGet(() -> new AutofuzzSeedSerializer(method)); 63 } 64 } 65 } 66 67 final class ByteArraySeedSerializer implements SeedSerializer { 68 @Override read(byte[] bytes)69 public Object[] read(byte[] bytes) { 70 return new Object[] {bytes}; 71 } 72 73 @Override write(Object[] args)74 public byte[] write(Object[] args) { 75 return (byte[]) args[0]; 76 } 77 } 78 79 final class FuzzedDataProviderSeedSerializer implements SeedSerializer { 80 @Override read(byte[] bytes)81 public Object[] read(byte[] bytes) { 82 return new Object[] {FuzzedDataProviderImpl.withJavaData(bytes)}; 83 } 84 85 @Override write(Object[] args)86 public byte[] write(Object[] args) throws UnsupportedOperationException { 87 // While we could get the underlying bytes, it's not possible to provide Java seeds for fuzz 88 // tests with a FuzzedDataProvider parameter. 89 throw new UnsupportedOperationException(); 90 } 91 } 92 93 final class ArgumentsMutatorSeedSerializer implements SeedSerializer { 94 private final ArgumentsMutator mutator; 95 private boolean allReadsValid; 96 ArgumentsMutatorSeedSerializer(ArgumentsMutator mutator)97 public ArgumentsMutatorSeedSerializer(ArgumentsMutator mutator) { 98 this.mutator = mutator; 99 } 100 101 @Override read(byte[] bytes)102 public Object[] read(byte[] bytes) { 103 allReadsValid &= mutator.read(new ByteArrayInputStream(bytes)); 104 return mutator.getArguments(); 105 } 106 107 @Override allReadsValid()108 public boolean allReadsValid() { 109 return allReadsValid; 110 } 111 112 @Override write(Object[] args)113 public byte[] write(Object[] args) { 114 ByteArrayOutputStream out = new ByteArrayOutputStream(); 115 mutator.writeAny(out, args); 116 return out.toByteArray(); 117 } 118 } 119 120 final class AutofuzzSeedSerializer implements SeedSerializer { 121 private final Meta meta; 122 private final Method method; 123 AutofuzzSeedSerializer(Method method)124 public AutofuzzSeedSerializer(Method method) { 125 this.meta = new Meta(method.getDeclaringClass()); 126 this.method = method; 127 } 128 129 @Override read(byte[] bytes)130 public Object[] read(byte[] bytes) { 131 try (FuzzedDataProviderImpl data = FuzzedDataProviderImpl.withJavaData(bytes)) { 132 // The Autofuzz FuzzTarget uses data to construct an instance of the test class before 133 // it constructs the fuzz test arguments. We don't need the instance here, but still 134 // generate it as that mutates the FuzzedDataProvider state. 135 meta.consumeNonStatic(data, method.getDeclaringClass()); 136 return meta.consumeArguments(data, method, null); 137 } 138 } 139 140 @Override write(Object[] args)141 public byte[] write(Object[] args) throws UnsupportedOperationException { 142 throw new UnsupportedOperationException(); 143 } 144 } 145