Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8254742

InputStream::readNBytes(int) result may contain zeros not in input

    XMLWordPrintable

    Details

    • Subcomponent:
    • Resolved In Build:
      b23
    • Verification:
      Verified

      Description

      ADDITIONAL SYSTEM INFORMATION :
      Linux Manjaro 20.1.1 Mikah /
      OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)

      A DESCRIPTION OF THE PROBLEM :
      The implementation of the method InputStream#readNBytes(int) read several intermediate arrays of bytes of fixed size (8192 according to the source) and finally concatenate all of them to form the final array of bytes.

      Since the implementation does not handle the case when the read(b[],off,len) returns 0, the implementation assumes that each intermediate arrays have been entirely filled with data from the inputStream. This assumption is invalid resulting in some case of invalid 0 values in the middle of the final array of bytes.

      For instance, for an AudioInputStream with a frameSize of 6 (for Audio WAV File with a 24 bit sampling), the method read(byte[],int,int) will return 0 for any requested length lower than 6.

      Since 8192 = 2 + 8190 = 2 + 6*1365, the intermediate buffer in the method readNBytes(int) will only be filed by 8190 bytes and the result will have invalid 0 value at indexes 8190 and 8191.




      ---------- BEGIN SOURCE ----------
      package jrebug;

      import java.io.IOException;
      import java.io.InputStream;
      import java.util.Arrays;

      public class Main {

          private static final byte BYTE_VALUE = 0x20;

          private static final int DEFAULT_BUFFER_SIZE_IN_INPUT_STREAM = 8192;

          public static void main(String[] args) throws IOException {
              TestReadNBytes.test(6);
          }

          private static class TestReadNBytes {

              public static void test(int frameSize) throws IOException {
                  new TestReadNBytes(frameSize).test();
              }

              private final int frameSize;

              private final int nbFrames;

              private InputStream inputStream = null;

              private byte[] readData = null;

              private TestReadNBytes(int frameSize) {
                  this.frameSize = frameSize;
                  this.nbFrames = DEFAULT_BUFFER_SIZE_IN_INPUT_STREAM/frameSize+1;
              }

              public void test() throws IOException {
                  this.createInputStream();
                  this.readAllBytesFromInputStream();
                  this.checkThatAllReadDataAreValid();
              }

              private void createInputStream() {
                  this.inputStream = new ConstantByChunked(frameSize,nbFrames);
              }

              private void readAllBytesFromInputStream() throws IOException {
                  readData = this.inputStream.readNBytes(frameSize*nbFrames);
              }

              private void checkThatAllReadDataAreValid() {
                  for (int i = 0; i < readData.length; i++) {
                      if (readData[i] != BYTE_VALUE) {
                          throw new RuntimeException("Invalid byte value at index="+i);
                      }
                  }
              }
          }


          /**
           * A very basic input stream that return only bytes equal to {@link #BYTE_VALUE}
           * but by chunks.
           */
          public static class ConstantByChunked extends InputStream {

              private final int size;

              private final int frameSize;

              private int pos =0;

              public ConstantByChunked(int frameSize, int nbFrames) {
                  this.frameSize = frameSize;
                  this.size = frameSize*nbFrames;
              }

              @Override
              public int read() throws IOException {
                  if (frameSize != 1) {
                      throw new IOException("cannot read a single byte if frame size > 1");
                  }
                  if (pos<size) {
                      pos++;
                      return BYTE_VALUE&0xff;
                  }
                  return -1;
              }

              @Override
              public int read(byte[] b, int off, int len) throws IOException {
                  final int reminder = len % frameSize;
                  if (reminder != 0) {
                      len -= reminder;
                      if (len == 0) {
                          return 0;
                      }
                  }
                  if (pos >= size) {
                      return -1;
                  }

                  int avail = size - pos;
                  if (len > avail) {
                      len = avail;
                  }
                  if (len <= 0) {
                      return 0;
                  }
                  Arrays.fill(b,off,off+len,BYTE_VALUE);
                  pos += len;
                  return len;
              }
          }


          public static void willBlock() throws IOException {
              try (InputStream stream = new ConstantByChunked(6,2000)) {
                  final byte[] data = stream.readNBytes(2);
                  System.out.println("Will never be printed : "+data.length);
              }
          }

      }
      ---------- END SOURCE ----------

      FREQUENCY : always


        Attachments

          Issue Links

            Activity

              People

              Assignee:
              bpb Brian Burkhalter
              Reporter:
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: