Skip to content

Commit a56a157

Browse files
authored
Fix decompression of empty messages with a ratio limit (#2246)
Motivation: The decompressor has a decompression limit to protect against zip bombs. This can either be absolute or ratio based. It's also possible in gRPC for a zero length message to be marked as compressed. gRPC attempts to decompress the zero length message and fails (because zlib wants a non-zero sized buffer and gRPC won't give it one as the limit is the buffer size is limited by the `ratio * msg_size` which in this case is zero). Modifications: - If the input to decompress has no length, skip decompression altogether Result: - Can decompress zero length payloads with the ratio limit - Resolves #2245
1 parent 67ae061 commit a56a157

File tree

2 files changed

+18
-0
lines changed

2 files changed

+18
-0
lines changed

Sources/GRPC/Compression/Zlib.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@ enum Zlib {
265265
/// - Returns: The number of bytes written into `output`.
266266
@discardableResult
267267
func inflate(_ input: inout ByteBuffer, into output: inout ByteBuffer) throws -> Int {
268+
if input.readableBytes == 0 {
269+
// Zero length compressed messages are always empty messages. Skip the inflate step
270+
// below and just return the number of bytes we wrote.
271+
return 0
272+
}
273+
268274
return try input.readWithUnsafeMutableReadableBytes { inputPointer -> (Int, Int) in
269275
// Setup the input buffer.
270276
self.stream.availableInputBytes = inputPointer.count

Tests/GRPCTests/ZlibTests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,16 @@ class ZlibTests: GRPCTestCase {
207207
let ratio: DecompressionLimit = .ratio(2)
208208
XCTAssertEqual(ratio.maximumDecompressedSize(compressedSize: 10), 20)
209209
}
210+
211+
func testDecompressEmptyPayload() throws {
212+
for limit in [DecompressionLimit.ratio(1), .absolute(1)] {
213+
for format in [Zlib.CompressionFormat.deflate, .gzip] {
214+
var compressed = self.allocator.buffer(capacity: 0)
215+
let inflate = Zlib.Inflate(format: format, limit: limit)
216+
var output = self.allocator.buffer(capacity: 0)
217+
XCTAssertEqual(try inflate.inflate(&compressed, into: &output), 0)
218+
XCTAssertEqual(output.readableBytes, 0)
219+
}
220+
}
221+
}
210222
}

0 commit comments

Comments
 (0)