JdkZlibDecoder may corrupt data when GZIP footer is fragmented (#11521)

Motivation:

Due a bug in the statemachine we produced an decoding error when the GZIP footer was fragmented in some cases

Modifications:

- Fix statemachine
- Add testcase

Result:

Correctly decode GZIP in all cases
This commit is contained in:
Norman Maurer 2021-07-28 09:15:37 +02:00
parent fc922c98d8
commit 0b7873bb82
2 changed files with 38 additions and 4 deletions

View File

@ -205,12 +205,15 @@ public class JdkZlibDecoder extends ZlibDecoder {
if (gzipState != GzipState.HEADER_END) { if (gzipState != GzipState.HEADER_END) {
if (gzipState == GzipState.FOOTER_START) { if (gzipState == GzipState.FOOTER_START) {
if (!handleGzipFooter(in)) { if (!handleGzipFooter(in)) {
// Either there was not enough data or the input is finished.
return; return;
} }
} else { // If we consumed the footer we will start with the header again.
if (!readGZIPHeader(in)) { assert gzipState == GzipState.HEADER_START;
return; }
} if (!readGZIPHeader(in)) {
// There was not enough data readable to read the GZIP header.
return;
} }
// Some bytes may have been consumed, and so we must re-set the number of readable bytes. // Some bytes may have been consumed, and so we must re-set the number of readable bytes.
readableBytes = in.readableBytes(); readableBytes = in.readableBytes();

View File

@ -597,6 +597,37 @@ public class JdkZlibTest {
} }
} }
@Test
public void testDecodeWithHeaderFollowingFooter() throws Exception {
byte[] bytes = new byte[1024];
ThreadLocalRandom.current().nextBytes(bytes);
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
GZIPOutputStream out = new GZIPOutputStream(bytesOut);
out.write(bytes);
out.close();
byte[] compressed = bytesOut.toByteArray();
ByteBuf buffer = Unpooled.buffer().writeBytes(compressed).writeBytes(compressed);
EmbeddedChannel channel = new EmbeddedChannel(new JdkZlibDecoder(ZlibWrapper.GZIP, true));
// Write it into the Channel in a way that we were able to decompress the first data completely but not the
// whole footer.
assertTrue(channel.writeInbound(buffer.readRetainedSlice(compressed.length - 1)));
assertTrue(channel.writeInbound(buffer));
assertTrue(channel.finish());
ByteBuf uncompressedBuffer = Unpooled.wrappedBuffer(bytes);
ByteBuf read = channel.readInbound();
assertEquals(uncompressedBuffer, read);
read.release();
read = channel.readInbound();
assertEquals(uncompressedBuffer, read);
read.release();
assertNull(channel.readInbound());
uncompressedBuffer.release();
}
private static byte[] gzip(byte[] bytes) throws IOException { private static byte[] gzip(byte[] bytes) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream stream = new GZIPOutputStream(out); GZIPOutputStream stream = new GZIPOutputStream(out);