authorgravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2025-03-31 14:33:33+11:00
committergravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2025-03-31 17:22:44+11:00
log7a7d6a02a58f78d19769829df74f911448ebc3d7
tree0ce567500ab9346ab1c204bbdf3e39229a1be1ba
parentacfdad858138de029abcb1c9bf20df0e58738eb3

std.compress.zstd: fix OOB access in literal decode

When decoding the literals section of a compressed block, the length of the regenerated size of the literals must be checked against the buffer literals are decoded into.

2 files changed, 20 insertions(+), 0 deletions(-)

lib/std/compress/zstandard.zig+19
......@@ -289,3 +289,22 @@ test "zero sized block" {
289289 try expectEqualDecodedStreaming("", input_raw);
290290 try expectEqualDecodedStreaming("", input_rle);
291291}
292
293test "declared raw literals size too large" {
294 const input_raw =
295 "\x28\xb5\x2f\xfd" ++ // zstandard frame magic number
296 "\x00\x00" ++ // frame header: everything unset, window descriptor zero
297 "\x95\x00\x00" ++ // block header with: last_block set, block_type compressed, block_size 18
298 "\xbc\xf3\xae" ++ // literals section header with: type raw, size_format 3, regenerated_size 716603
299 "\xa5\x9f\xe3"; // some bytes of literal content - the content is shorter than regenerated_size
300
301 // Note that the regenerated_size in the above input is larger than block maximum size, so the
302 // block can't be valid as it is a raw literals block.
303
304 var fbs = std.io.fixedBufferStream(input_raw);
305 var window: [1024]u8 = undefined;
306 var stream = decompressor(fbs.reader(), .{ .window_buffer = &window });
307
308 var buf: [1024]u8 = undefined;
309 try std.testing.expectError(error.MalformedBlock, stream.read(&buf));
310}
lib/std/compress/zstandard/decode/block.zig+1
......@@ -989,6 +989,7 @@ pub fn decodeLiteralsSection(
989989 const header = try decodeLiteralsHeader(source);
990990 switch (header.block_type) {
991991 .raw => {
992 if (buffer.len < header.regenerated_size) return error.LiteralsBufferTooSmall;
992993 try source.readNoEof(buffer[0..header.regenerated_size]);
993994 return LiteralsSection{
994995 .header = header,