authorgravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2023-01-24 13:07:58+11:00
committergravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2023-02-20 09:09:06+11:00
log6b85373875e2a7a8ef9d74e20e8e827dc622a29c
treedcc3a338c44af76e06fa1adeab6bdeb4380682fa
parent082acd7f17358ed3a7787f284b48b754fefd8187

std.compress.zstandard: validate sequence lengths


1 files changed, 26 insertions(+), 12 deletions(-)

lib/std/compress/zstandard/decompress.zig+26-12
......@@ -271,10 +271,9 @@ pub const DecodeState = struct {
271271 literals: LiteralsSection,
272272 sequence: Sequence,
273273 ) !void {
274 try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length);
274 if (sequence.offset > write_pos + sequence.literal_length) return error.MalformedSequence;
275275
276 // TODO: should we validate offset against max_window_size?
277 assert(sequence.offset <= write_pos + sequence.literal_length);
276 try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length);
278277 const copy_start = write_pos + sequence.literal_length - sequence.offset;
279278 const copy_end = copy_start + sequence.match_length;
280279 // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr
......@@ -288,8 +287,9 @@ pub const DecodeState = struct {
288287 literals: LiteralsSection,
289288 sequence: Sequence,
290289 ) !void {
290 if (sequence.offset > dest.data.len) return error.MalformedSequence;
291
291292 try self.decodeLiteralsRingBuffer(dest, literals, sequence.literal_length);
292 // TODO: check that ring buffer window is full enough for match copies
293293 const copy_slice = dest.sliceAt(dest.write_index + dest.data.len - sequence.offset, sequence.match_length);
294294 // TODO: would std.mem.copy and figuring out dest slice be better/faster?
295295 for (copy_slice.first) |b| dest.writeAssumeCapacity(b);
......@@ -302,9 +302,13 @@ pub const DecodeState = struct {
302302 write_pos: usize,
303303 literals: LiteralsSection,
304304 bit_reader: anytype,
305 sequence_size_limit: usize,
305306 last_sequence: bool,
306307 ) !usize {
307308 const sequence = try self.nextSequence(bit_reader);
309 const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length;
310 if (sequence_length > sequence_size_limit) return error.MalformedSequence;
311
308312 try self.executeSequenceSlice(dest, write_pos, literals, sequence);
309313 log.debug("sequence decompressed into '{x}'", .{
310314 std.fmt.fmtSliceHexUpper(dest[write_pos .. write_pos + sequence.literal_length + sequence.match_length]),
......@@ -314,7 +318,7 @@ pub const DecodeState = struct {
314318 try self.updateState(.match, bit_reader);
315319 try self.updateState(.offset, bit_reader);
316320 }
317 return sequence.match_length + sequence.literal_length;
321 return sequence_length;
318322 }
319323
320324 pub fn decodeSequenceRingBuffer(
......@@ -322,12 +326,15 @@ pub const DecodeState = struct {
322326 dest: *RingBuffer,
323327 literals: LiteralsSection,
324328 bit_reader: anytype,
329 sequence_size_limit: usize,
325330 last_sequence: bool,
326331 ) !usize {
327332 const sequence = try self.nextSequence(bit_reader);
333 const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length;
334 if (sequence_length > sequence_size_limit) return error.MalformedSequence;
335
328336 try self.executeSequenceRingBuffer(dest, literals, sequence);
329337 if (std.options.log_level == .debug) {
330 const sequence_length = sequence.literal_length + sequence.match_length;
331338 const written_slice = dest.sliceLast(sequence_length);
332339 log.debug("sequence decompressed into '{x}{x}'", .{
333340 std.fmt.fmtSliceHexUpper(written_slice.first),
......@@ -339,7 +346,7 @@ pub const DecodeState = struct {
339346 try self.updateState(.match, bit_reader);
340347 try self.updateState(.offset, bit_reader);
341348 }
342 return sequence.match_length + sequence.literal_length;
349 return sequence_length;
343350 }
344351
345352 fn nextLiteralMultiStream(self: *DecodeState, literals: LiteralsSection) !void {
......@@ -717,9 +724,9 @@ pub fn decodeBlock(
717724 consumed_count: *usize,
718725 written_count: usize,
719726) !usize {
720 const block_maximum_size = 1 << 17; // 128KiB
727 const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB
721728 const block_size = block_header.block_size;
722 if (block_maximum_size < block_size) return error.BlockSizeOverMaximum;
729 if (block_size_max < block_size) return error.BlockSizeOverMaximum;
723730 // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks)
724731 switch (block_header.block_type) {
725732 .raw => return decodeRawBlock(dest[written_count..], src, block_size, consumed_count),
......@@ -739,17 +746,21 @@ pub fn decodeBlock(
739746
740747 try decode_state.readInitialFseState(&bit_stream);
741748
749 var sequence_size_limit = block_size_max;
742750 var i: usize = 0;
743751 while (i < sequences_header.sequence_count) : (i += 1) {
744752 log.debug("decoding sequence {d}", .{i});
753 const write_pos = written_count + bytes_written;
745754 const decompressed_size = try decode_state.decodeSequenceSlice(
746755 dest,
747 written_count + bytes_written,
756 write_pos,
748757 literals,
749758 &bit_stream,
759 sequence_size_limit,
750760 i == sequences_header.sequence_count - 1,
751761 );
752762 bytes_written += decompressed_size;
763 sequence_size_limit -= decompressed_size;
753764 }
754765
755766 bytes_read += bit_stream_bytes.len;
......@@ -781,10 +792,10 @@ pub fn decodeBlockRingBuffer(
781792 block_header: frame.ZStandard.Block.Header,
782793 decode_state: *DecodeState,
783794 consumed_count: *usize,
784 block_size_maximum: usize,
795 block_size_max: usize,
785796) !usize {
786797 const block_size = block_header.block_size;
787 if (block_size_maximum < block_size) return error.BlockSizeOverMaximum;
798 if (block_size_max < block_size) return error.BlockSizeOverMaximum;
788799 // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks)
789800 switch (block_header.block_type) {
790801 .raw => return decodeRawBlockRingBuffer(dest, src, block_size, consumed_count),
......@@ -804,6 +815,7 @@ pub fn decodeBlockRingBuffer(
804815
805816 try decode_state.readInitialFseState(&bit_stream);
806817
818 var sequence_size_limit = block_size_max;
807819 var i: usize = 0;
808820 while (i < sequences_header.sequence_count) : (i += 1) {
809821 log.debug("decoding sequence {d}", .{i});
......@@ -811,9 +823,11 @@ pub fn decodeBlockRingBuffer(
811823 dest,
812824 literals,
813825 &bit_stream,
826 sequence_size_limit,
814827 i == sequences_header.sequence_count - 1,
815828 );
816829 bytes_written += decompressed_size;
830 sequence_size_limit -= decompressed_size;
817831 }
818832
819833 bytes_read += bit_stream_bytes.len;