• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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