Skip to content

Commit

Permalink
Fix gzip decoding when FLG.FHCRC is set (#11805)
Browse files Browse the repository at this point in the history
 
Motivation:

The current implementation is not respecting the spec at https://www.ietf.org/rfc/rfc1952, section 2.3.
> If FHCRC is set, a CRC16 for the gzip header is present,
> immediately before the compressed data. The CRC16 consists
> of the two least significant bytes of the CRC32 for all
> bytes of the gzip header up to and not including the CRC16.

The implementation is reusing the CRC32 logic without taking the two least significant bytes.

Modifications:

Use a method that respects the spec.
 
Result:

When the FLG.FHCRC bit is set, the correct verification algorithm will be used.
  • Loading branch information
AL333Z authored and normanmaurer committed Oct 29, 2021
1 parent af303bf commit cdf13d7
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 1 deletion.
Expand Up @@ -409,7 +409,7 @@ private boolean readGZIPHeader(ByteBuf in) {
// fall through
case PROCESS_FHCRC:
if ((flags & FHCRC) != 0) {
if (!verifyCrc(in)) {
if (!verifyCrc16(in)) {
return false;
}
}
Expand Down Expand Up @@ -499,6 +499,25 @@ private boolean verifyCrc(ByteBuf in) {
return true;
}

private boolean verifyCrc16(ByteBuf in) {
if (in.readableBytes() < 2) {
return false;
}
long readCrc32 = crc.getValue();
long crc16Value = 0;
long readCrc16 = 0; // the two least significant bytes from the CRC32
for (int i = 0; i < 2; ++i) {
crc16Value |= (long) in.readUnsignedByte() << (i * 8);
readCrc16 |= ((readCrc32 >> (i * 8)) & 0xff) << (i * 8);
}

if (crc16Value != readCrc16) {
throw new DecompressionException(
"CRC16 value mismatch. Expected: " + crc16Value + ", Got: " + readCrc16);
}
return true;
}

/*
* Returns true if the cmf_flg parameter (think: first two bytes of a zlib stream)
* indicates that this is a zlib stream.
Expand Down
Expand Up @@ -631,6 +631,50 @@ public void testDecodeWithHeaderFollowingFooter() throws Exception {
uncompressedBuffer.release();
}

@Test
public void testGZIP3() throws Exception {
byte[] bytes = "Foo".getBytes(CharsetUtil.UTF_8);
ByteBuf data = Unpooled.wrappedBuffer(bytes);
ByteBuf deflatedData = Unpooled.wrappedBuffer(
new byte[]{
31, -117, // magic number
8, // CM
2, // FLG.FHCRC
0, 0, 0, 0, // MTIME
0, // XFL
7, // OS
-66, -77, // CRC16
115, -53, -49, 7, 0, // compressed blocks
-63, 35, 62, -76, // CRC32
3, 0, 0, 0 // ISIZE
}
);

EmbeddedChannel chDecoderGZip = new EmbeddedChannel(createDecoder(ZlibWrapper.GZIP));
try {
while (deflatedData.isReadable()) {
chDecoderGZip.writeInbound(deflatedData.readRetainedSlice(1));
}
deflatedData.release();
assertTrue(chDecoderGZip.finish());
ByteBuf buf = Unpooled.buffer();
for (;;) {
ByteBuf b = chDecoderGZip.readInbound();
if (b == null) {
break;
}
buf.writeBytes(b);
b.release();
}
assertEquals(buf, data);
assertNull(chDecoderGZip.readInbound());
data.release();
buf.release();
} finally {
dispose(chDecoderGZip);
}
}

private static byte[] gzip(byte[] bytes) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream stream = new GZIPOutputStream(out);
Expand Down

0 comments on commit cdf13d7

Please sign in to comment.