This is an automated email from the ASF dual-hosted git repository.

qiaojialin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new c4f4eb9  [IOTDB-2068] GZIP compressor meets 
ArrayIndexOutOfBoundsException (#4488)
c4f4eb9 is described below

commit c4f4eb948644e221fd6b549fdcbdd7fcc8339156
Author: Alan Choo <[email protected]>
AuthorDate: Sun Nov 28 01:10:46 2021 +0800

    [IOTDB-2068] GZIP compressor meets ArrayIndexOutOfBoundsException (#4488)
---
 .../apache/iotdb/tsfile/compress/ICompressor.java  | 58 +++++++++++++++++++++-
 .../compress/GZIPCompressOverflowException.java    | 26 ++++++++++
 .../apache/iotdb/tsfile/write/page/PageWriter.java |  4 ++
 .../iotdb/tsfile/write/page/TimePageWriter.java    |  4 ++
 .../iotdb/tsfile/write/page/ValuePageWriter.java   |  4 ++
 .../org/apache/iotdb/tsfile/compress/GZIPTest.java | 17 ++++++-
 .../org/apache/iotdb/tsfile/compress/LZ4Test.java  | 17 ++++++-
 .../apache/iotdb/tsfile/compress/SnappyTest.java   | 17 ++++++-
 8 files changed, 142 insertions(+), 5 deletions(-)

diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/compress/ICompressor.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/compress/ICompressor.java
index 66d18f9..9243e53 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/compress/ICompressor.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/compress/ICompressor.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.tsfile.compress;
 
 import 
org.apache.iotdb.tsfile.exception.compress.CompressionTypeNotSupportedException;
+import 
org.apache.iotdb.tsfile.exception.compress.GZIPCompressOverflowException;
 import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
 
 import net.jpountz.lz4.LZ4Compressor;
@@ -72,6 +73,15 @@ public interface ICompressor extends Serializable {
   byte[] compress(byte[] data) throws IOException;
 
   /**
+   * abstract method of compress. this method has an important overhead due to 
the fact that it
+   * needs to allocate a byte array to compress into, and then needs to resize 
this buffer to the
+   * actual compressed length.
+   *
+   * @return byte array of compressed data.
+   */
+  byte[] compress(byte[] data, int offset, int length) throws IOException;
+
+  /**
    * abstract method of compress.
    *
    * @return byte length of compressed data.
@@ -87,6 +97,13 @@ public interface ICompressor extends Serializable {
    */
   int compress(ByteBuffer data, ByteBuffer compressed) throws IOException;
 
+  /**
+   * Get the maximum byte size needed for compressing data of the given byte 
size. For GZIP, this
+   * method is insecure and may cause {@code GZIPCompressOverflowException}
+   *
+   * @param uncompressedDataSize byte size of the data to compress
+   * @return maximum byte size of the compressed data
+   */
   int getMaxBytesForCompression(int uncompressedDataSize);
 
   CompressionType getType();
@@ -100,6 +117,11 @@ public interface ICompressor extends Serializable {
     }
 
     @Override
+    public byte[] compress(byte[] data, int offset, int length) throws 
IOException {
+      throw new IOException("No Compressor does not support compression 
function");
+    }
+
+    @Override
     public int compress(byte[] data, int offset, int length, byte[] 
compressed) throws IOException {
       throw new IOException("No Compressor does not support compression 
function");
     }
@@ -131,6 +153,20 @@ public interface ICompressor extends Serializable {
     }
 
     @Override
+    public byte[] compress(byte[] data, int offset, int length) throws 
IOException {
+      byte[] maxCompressed = new byte[getMaxBytesForCompression(length)];
+      int compressedSize = Snappy.compress(data, offset, length, 
maxCompressed, 0);
+      byte[] compressed = null;
+      if (compressedSize < maxCompressed.length) {
+        compressed = new byte[compressedSize];
+        System.arraycopy(maxCompressed, 0, compressed, 0, compressedSize);
+      } else {
+        compressed = maxCompressed;
+      }
+      return compressed;
+    }
+
+    @Override
     public int compress(byte[] data, int offset, int length, byte[] 
compressed) throws IOException {
       return Snappy.compress(data, offset, length, compressed, 0);
     }
@@ -169,6 +205,11 @@ public interface ICompressor extends Serializable {
     }
 
     @Override
+    public byte[] compress(byte[] data, int offset, int length) throws 
IOException {
+      return compressor.compress(data, offset, length);
+    }
+
+    @Override
     public int compress(byte[] data, int offset, int length, byte[] 
compressed) {
       return compressor.compress(data, offset, length, compressed, 0);
     }
@@ -226,23 +267,36 @@ public interface ICompressor extends Serializable {
     }
 
     @Override
+    public byte[] compress(byte[] data, int offset, int length) throws 
IOException {
+      byte[] dataBefore = new byte[length];
+      System.arraycopy(data, offset, dataBefore, 0, length);
+      return GZIPCompress.compress(dataBefore);
+    }
+
+    /** @exception GZIPCompressOverflowException if compressed byte array is 
too small. */
+    @Override
     public int compress(byte[] data, int offset, int length, byte[] 
compressed) throws IOException {
       byte[] dataBefore = new byte[length];
       System.arraycopy(data, offset, dataBefore, 0, length);
       byte[] res = GZIPCompress.compress(dataBefore);
+      if (res.length > compressed.length) {
+        throw new GZIPCompressOverflowException();
+      }
       System.arraycopy(res, 0, compressed, 0, res.length);
       return res.length;
     }
 
+    /** @exception GZIPCompressOverflowException if compressed ByteBuffer is 
too small. */
     @Override
     public int compress(ByteBuffer data, ByteBuffer compressed) throws 
IOException {
       int length = data.remaining();
       byte[] dataBefore = new byte[length];
       data.get(dataBefore, 0, length);
-
       byte[] res = GZIPCompress.compress(dataBefore);
+      if (res.length > compressed.capacity()) {
+        throw new GZIPCompressOverflowException();
+      }
       compressed.put(res);
-
       return res.length;
     }
 
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/compress/GZIPCompressOverflowException.java
 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/compress/GZIPCompressOverflowException.java
new file mode 100644
index 0000000..9e54818
--- /dev/null
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/compress/GZIPCompressOverflowException.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.iotdb.tsfile.exception.compress;
+
+public class GZIPCompressOverflowException extends RuntimeException {
+
+  public GZIPCompressOverflowException() {
+    super("compressed data is larger than the given byte container.");
+  }
+}
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/PageWriter.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/PageWriter.java
index f003825..0401908 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/PageWriter.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/PageWriter.java
@@ -216,6 +216,10 @@ public class PageWriter {
 
     if (compressor.getType().equals(CompressionType.UNCOMPRESSED)) {
       compressedSize = uncompressedSize;
+    } else if (compressor.getType().equals(CompressionType.GZIP)) {
+      compressedBytes =
+          compressor.compress(pageData.array(), pageData.position(), 
uncompressedSize);
+      compressedSize = compressedBytes.length;
     } else {
       compressedBytes = new 
byte[compressor.getMaxBytesForCompression(uncompressedSize)];
       // data is never a directByteBuffer now, so we can use data.array()
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/TimePageWriter.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/TimePageWriter.java
index 5223d18..1c668fc 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/TimePageWriter.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/TimePageWriter.java
@@ -107,6 +107,10 @@ public class TimePageWriter {
 
     if (compressor.getType().equals(CompressionType.UNCOMPRESSED)) {
       compressedSize = uncompressedSize;
+    } else if (compressor.getType().equals(CompressionType.GZIP)) {
+      compressedBytes =
+          compressor.compress(pageData.array(), pageData.position(), 
uncompressedSize);
+      compressedSize = compressedBytes.length;
     } else {
       compressedBytes = new 
byte[compressor.getMaxBytesForCompression(uncompressedSize)];
       // data is never a directByteBuffer now, so we can use data.array()
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/ValuePageWriter.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/ValuePageWriter.java
index d707886..0328a56 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/ValuePageWriter.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/page/ValuePageWriter.java
@@ -237,6 +237,10 @@ public class ValuePageWriter {
 
     if (compressor.getType().equals(CompressionType.UNCOMPRESSED)) {
       compressedSize = uncompressedSize;
+    } else if (compressor.getType().equals(CompressionType.GZIP)) {
+      compressedBytes =
+          compressor.compress(pageData.array(), pageData.position(), 
uncompressedSize);
+      compressedSize = compressedBytes.length;
     } else {
       compressedBytes = new 
byte[compressor.getMaxBytesForCompression(uncompressedSize)];
       // data is never a directByteBuffer now, so we can use data.array()
diff --git 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/GZIPTest.java 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/GZIPTest.java
index bd16ad7..04d14b6 100644
--- a/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/GZIPTest.java
+++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/GZIPTest.java
@@ -52,7 +52,7 @@ public class GZIPTest {
   public void tearDown() {}
 
   @Test
-  public void testBytes() throws IOException {
+  public void testBytes1() throws IOException {
     int n = 500000;
     String input = randomString(n);
     byte[] uncom = input.getBytes(StandardCharsets.UTF_8);
@@ -63,6 +63,21 @@ public class GZIPTest {
   }
 
   @Test
+  public void testBytes2() throws IOException {
+    ICompressor.GZIPCompressor compressor = new ICompressor.GZIPCompressor();
+    IUnCompressor.GZIPUnCompressor unCompressor = new 
IUnCompressor.GZIPUnCompressor();
+
+    int n = 500000;
+    String input = randomString(n);
+    byte[] uncom = input.getBytes(StandardCharsets.UTF_8);
+    byte[] compressed = compressor.compress(uncom, 0, uncom.length);
+    // length should be same
+    Assert.assertEquals(compressor.compress(uncom).length, compressed.length);
+    byte[] uncompressed = unCompressor.uncompress(compressed);
+    Assert.assertArrayEquals(uncom, uncompressed);
+  }
+
+  @Test
   public void testByteBuffer() throws IOException {
     for (int i = 1; i < 500000; i += 100000) {
       String input = randomString(i);
diff --git a/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/LZ4Test.java 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/LZ4Test.java
index 005cfe8..8e3aea1 100644
--- a/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/LZ4Test.java
+++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/LZ4Test.java
@@ -47,7 +47,7 @@ public class LZ4Test {
   public void tearDown() {}
 
   @Test
-  public void testBytes() throws IOException {
+  public void testBytes1() throws IOException {
     String input = randomString(2000000);
     byte[] uncom = input.getBytes(StandardCharsets.UTF_8);
     long time = System.currentTimeMillis();
@@ -65,4 +65,19 @@ public class LZ4Test {
 
     Assert.assertArrayEquals(uncom, uncompressed);
   }
+
+  @Test
+  public void testBytes2() throws IOException {
+    ICompressor.IOTDBLZ4Compressor compressor = new 
ICompressor.IOTDBLZ4Compressor();
+    IUnCompressor.LZ4UnCompressor unCompressor = new 
IUnCompressor.LZ4UnCompressor();
+
+    int n = 500000;
+    String input = randomString(n);
+    byte[] uncom = input.getBytes(StandardCharsets.UTF_8);
+    byte[] compressed = compressor.compress(uncom, 0, uncom.length);
+    // length should be same
+    Assert.assertEquals(compressor.compress(uncom).length, compressed.length);
+    byte[] uncompressed = unCompressor.uncompress(compressed);
+    Assert.assertArrayEquals(uncom, uncompressed);
+  }
 }
diff --git 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/SnappyTest.java 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/SnappyTest.java
index 57f7e50..3bd26b6 100644
--- a/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/SnappyTest.java
+++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/SnappyTest.java
@@ -48,7 +48,7 @@ public class SnappyTest {
   public void tearDown() {}
 
   @Test
-  public void testBytes() throws IOException {
+  public void testBytes1() throws IOException {
     int n = 500000;
     String input = randomString(n);
     byte[] uncom = input.getBytes(StandardCharsets.UTF_8);
@@ -64,6 +64,21 @@ public class SnappyTest {
   }
 
   @Test
+  public void testBytes2() throws IOException {
+    ICompressor.SnappyCompressor compressor = new 
ICompressor.SnappyCompressor();
+    IUnCompressor.SnappyUnCompressor unCompressor = new 
IUnCompressor.SnappyUnCompressor();
+
+    int n = 500000;
+    String input = randomString(n);
+    byte[] uncom = input.getBytes(StandardCharsets.UTF_8);
+    byte[] compressed = compressor.compress(uncom, 0, uncom.length);
+    // length should be same
+    Assert.assertEquals(compressor.compress(uncom).length, compressed.length);
+    byte[] uncompressed = unCompressor.uncompress(compressed);
+    Assert.assertArrayEquals(uncom, uncompressed);
+  }
+
+  @Test
   public void testByteBuffer() throws IOException {
     String input = randomString(5000);
     ByteBuffer source = ByteBuffer.allocateDirect(input.getBytes().length);

Reply via email to