InputStreamFuzzer.java

// Copyright 2023 Google LLC
//
// 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.
//
////////////////////////////////////////////////////////////////////////////////
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.io.input.AutoCloseInputStream;
import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.io.input.BoundedInputStream;
import org.apache.commons.io.input.BrokenInputStream;
import org.apache.commons.io.input.CharSequenceInputStream;
import org.apache.commons.io.input.CircularInputStream;
import org.apache.commons.io.input.ClassLoaderObjectInputStream;
import org.apache.commons.io.input.CloseShieldInputStream;
import org.apache.commons.io.input.ClosedInputStream;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.io.input.DemuxInputStream;
import org.apache.commons.io.input.InfiniteCircularInputStream;
import org.apache.commons.io.input.MarkShieldInputStream;
import org.apache.commons.io.input.MemoryMappedFileInputStream;
import org.apache.commons.io.input.MessageDigestInputStream;
import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.io.input.QueueInputStream;
import org.apache.commons.io.input.ReadAheadInputStream;
import org.apache.commons.io.input.SwappedDataInputStream;
import org.apache.commons.io.input.TaggedInputStream;
import org.apache.commons.io.input.TeeInputStream;
import org.apache.commons.io.input.UncheckedFilterInputStream;
import org.apache.commons.io.input.UnixLineEndingInputStream;
import org.apache.commons.io.input.UnsynchronizedBufferedInputStream;
import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
import org.apache.commons.io.input.UnsynchronizedFilterInputStream;
import org.apache.commons.io.input.WindowsLineEndingInputStream;
import org.apache.commons.io.input.buffer.PeekableInputStream;
import org.apache.commons.io.serialization.ValidatingObjectInputStream;

/**
 * This fuzzer targets the read method of different InputStream implementation classes in the input
 * or input.buffer packages.
 */
public class InputStreamFuzzer {
  public static void fuzzerTestOneInput(FuzzedDataProvider data) {
    try {
      InputStream is = null;
      Integer size = data.remainingBytes();
      Boolean boolValue = data.consumeBoolean();
      Integer choice = data.consumeInt(1, 29);
      byte[] srcBuffer = data.consumeRemainingAsBytes();
      InputStream bais = new ByteArrayInputStream(srcBuffer);

      // Randomly create an InputStream implementation object
      switch (choice) {
        case 1:
          is = new PeekableInputStream(bais);
          ((PeekableInputStream) is).peek(srcBuffer);
          break;
        case 2:
          is = AutoCloseInputStream.builder().setInputStream(bais).get();
          break;
        case 3:
          is =
              BOMInputStream.builder()
                  .setInputStream(bais)
                  .setByteOrderMarks()
                  .setInclude(boolValue)
                  .get();
          break;
        case 4:
          is = new BoundedInputStream(bais);
          break;
        case 5:
          is = BrokenInputStream.INSTANCE;
          break;
        case 6:
          is =
              CharSequenceInputStream.builder()
                  .setInputStream(bais)
                  .setCharset(Charset.defaultCharset())
                  .get();
          break;
        case 7:
          is = new CircularInputStream(srcBuffer, (long) srcBuffer.length);
          break;
        case 8:
          is = new ClassLoaderObjectInputStream(InputStreamFuzzer.class.getClassLoader(), bais);
          break;
        case 9:
          is = CloseShieldInputStream.wrap(bais);
          break;
        case 10:
          is = ClosedInputStream.INSTANCE;
          break;
        case 11:
          is = new CountingInputStream(bais);
          break;
        case 12:
          is = new DemuxInputStream().bindStream(bais);
          break;
        case 13:
          is = new InfiniteCircularInputStream(srcBuffer);
          break;
        case 14:
          is = new MarkShieldInputStream(bais);
          break;
        case 15:
          is = MemoryMappedFileInputStream.builder().setInputStream(bais).get();
          break;
        case 16:
          is =
              MessageDigestInputStream.builder()
                  .setInputStream(bais)
                  .setMessageDigest("SHA-256")
                  .get();
          break;
        case 17:
          is = new NullInputStream();
          break;
        case 18:
          is =
              QueueInputStream.builder()
                  .setInputStream(bais)
                  .setBlockingQueue(null)
                  .setTimeout(null)
                  .get();
          break;
        case 19:
          is = ReadAheadInputStream.builder().setInputStream(bais).get();
          break;
        case 20:
          is = new SwappedDataInputStream(bais);
          break;
        case 21:
          is = new TaggedInputStream(bais);
          break;
        case 22:
          OutputStream baos = new ByteArrayOutputStream(srcBuffer.length);
          is = new TeeInputStream(bais, baos, boolValue);
          break;
        case 23:
          is = UncheckedFilterInputStream.builder().setInputStream(bais).get();
          break;
        case 24:
          is = new UnixLineEndingInputStream(bais, boolValue);
          break;
        case 25:
          is = UnsynchronizedBufferedInputStream.builder().setInputStream(bais).get();
          break;
        case 26:
          is = UnsynchronizedByteArrayInputStream.builder().setByteArray(srcBuffer).get();
          break;
        case 27:
          is = UnsynchronizedFilterInputStream.builder().setByteArray(srcBuffer).get();
          break;
        case 28:
          is = new WindowsLineEndingInputStream(bais, boolValue);
          break;
        case 29:
          is = new ValidatingObjectInputStream(bais);
          break;
      }

      if (is != null) {
        // Fuzz the read method of the created InputStream object
        byte[] buffer = new byte[size + 1];
        is.reset();
        if (is.available() > 0) {
          is.read(buffer, 0, size + 1);
        }
        is.close();
      }
    } catch (IOException
        | IllegalArgumentException
        | NoSuchAlgorithmException
        | UnsupportedOperationException e) {
      // Known exception
    }
  }
}