From 6de96278b24f7562c63ecf9e25bd31b7c4232abb Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 24 Oct 2024 13:21:23 +0200 Subject: [PATCH 01/10] move from tail calls to loop+match --- test-libz-rs-sys/src/inflate.rs | 37 + .../src/test-data/blow_up_the_stack_1.gz | Bin 0 -> 974117 bytes .../src/test-data/blow_up_the_stack_2.gz | Bin 0 -> 821267 bytes zlib-rs/src/inflate.rs | 1659 ++++++++--------- 4 files changed, 858 insertions(+), 838 deletions(-) create mode 100644 test-libz-rs-sys/src/test-data/blow_up_the_stack_1.gz create mode 100644 test-libz-rs-sys/src/test-data/blow_up_the_stack_2.gz diff --git a/test-libz-rs-sys/src/inflate.rs b/test-libz-rs-sys/src/inflate.rs index 3defafce..5725c3e2 100644 --- a/test-libz-rs-sys/src/inflate.rs +++ b/test-libz-rs-sys/src/inflate.rs @@ -1969,3 +1969,40 @@ fn issue_232() { unsafe { inflateEnd(&mut stream) }; } + +#[test] +fn blow_up_the_stack_1() { + // requires a sequence of states that would blow up the stack if inflate is not stack safe. + + const INPUT: &[u8] = include_bytes!("test-data/blow_up_the_stack_1.gz"); + + let mut output_ng = vec![0; INPUT.len() * 128]; + let mut output_rs = vec![0; INPUT.len() * 128]; + + let config = InflateConfig::default(); + + let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, &INPUT, config); + assert_eq!(err, ReturnCode::DataError); + + let (_, err) = uncompress_slice(&mut output_rs, &INPUT, config); + assert_eq!(err, ReturnCode::DataError); +} + +#[test] +#[cfg_attr(miri, ignore = "slow")] +fn blow_up_the_stack_2() { + // requires a sequence of states that would blow up the stack if inflate is not stack safe. + + const INPUT: &[u8] = include_bytes!("test-data/blow_up_the_stack_2.gz"); + + let mut output_ng = vec![0; INPUT.len() * 128]; + let mut output_rs = vec![0; INPUT.len() * 128]; + + let config = InflateConfig::default(); + + let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, &INPUT, config); + assert_eq!(err, ReturnCode::DataError); + + let (_, err) = uncompress_slice(&mut output_rs, &INPUT, config); + assert_eq!(err, ReturnCode::DataError); +} diff --git a/test-libz-rs-sys/src/test-data/blow_up_the_stack_1.gz b/test-libz-rs-sys/src/test-data/blow_up_the_stack_1.gz new file mode 100644 index 0000000000000000000000000000000000000000..f21a18da2e79f5e2ad0c64a310996157497a4fae GIT binary patch literal 974117 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

9_7D$gXAIBkbO2CCwCR8bx8X+Jr(}dKxloh)z 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

v28B|~(9WY>&k)tQqZUuhp`{+mC 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 literal 0 HcmV?d00001 diff --git a/test-libz-rs-sys/src/test-data/blow_up_the_stack_2.gz b/test-libz-rs-sys/src/test-data/blow_up_the_stack_2.gz new file mode 100644 index 0000000000000000000000000000000000000000..2f2556f13faf4283eeeb85bd21f041a208659449 GIT binary patch literal 821267 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 u1;ZYna8 State<'a> { fn dispatch(&mut self) -> ReturnCode { - match self.mode { - Mode::Head => self.head(), - Mode::Flags => self.flags(), - Mode::Time => self.time(), - Mode::Os => self.os(), - Mode::ExLen => self.ex_len(), - Mode::Extra => self.extra(), - Mode::Name => self.name(), - Mode::Comment => self.comment(), - Mode::HCrc => self.hcrc(), - Mode::Sync => self.sync(), - Mode::Type => self.type_(), - Mode::TypeDo => self.type_do(), - Mode::Stored => self.stored(), - Mode::CopyBlock => self.copy_block(), - Mode::Check => self.check(), - Mode::Len => self.len(), - Mode::Len_ => self.len_(), - Mode::LenExt => self.len_ext(), - Mode::Lit => self.lit(), - Mode::Dist => self.dist(), - Mode::DistExt => self.dist_ext(), - Mode::Match => self.match_(), - Mode::Done => todo!(), - Mode::Table => self.table(), - Mode::LenLens => self.len_lens(), - Mode::CodeLens => self.code_lens(), - Mode::Dict => self.dict(), - Mode::DictId => self.dict_id(), - Mode::Bad => self.bad("repeated call with bad state\0"), - Mode::Mem => self.mem(), - Mode::Length => self.length(), - } - } - - // ---------------- + 'label: loop { + match self.mode { + Mode::Head => { + if self.wrap == 0 { + self.mode = Mode::TypeDo; - /// Initial state - #[inline(never)] - fn head(&mut self) -> ReturnCode { - if self.wrap == 0 { - self.mode = Mode::TypeDo; - return self.type_do(); - } + continue 'label; + } - need_bits!(self, 16); + need_bits!(self, 16); - // Gzip - if (self.wrap & 2) != 0 && self.bit_reader.hold() == 0x8b1f { - if self.wbits == 0 { - self.wbits = 15; - } + // Gzip + if (self.wrap & 2) != 0 && self.bit_reader.hold() == 0x8b1f { + if self.wbits == 0 { + self.wbits = 15; + } - let b0 = self.bit_reader.bits(8) as u8; - let b1 = (self.bit_reader.hold() >> 8) as u8; - self.checksum = crc32(crate::CRC32_INITIAL_VALUE, &[b0, b1]); - self.bit_reader.init_bits(); + let b0 = self.bit_reader.bits(8) as u8; + let b1 = (self.bit_reader.hold() >> 8) as u8; + self.checksum = crc32(crate::CRC32_INITIAL_VALUE, &[b0, b1]); + self.bit_reader.init_bits(); - self.mode = Mode::Flags; - return self.flags(); - } + self.mode = Mode::Flags; - if let Some(header) = &mut self.head { - header.done = -1; - } - - // check if zlib header is allowed - if (self.wrap & 1) == 0 - || ((self.bit_reader.bits(8) << 8) + (self.bit_reader.hold() >> 8)) % 31 != 0 - { - self.mode = Mode::Bad; - return self.bad("incorrect header check\0"); - } + continue 'label; + } - if self.bit_reader.bits(4) != Z_DEFLATED as u64 { - self.mode = Mode::Bad; - return self.bad("unknown compression method\0"); - } + if let Some(header) = &mut self.head { + header.done = -1; + } - self.bit_reader.drop_bits(4); - let len = self.bit_reader.bits(4) as u8 + 8; + // check if zlib header is allowed + if (self.wrap & 1) == 0 + || ((self.bit_reader.bits(8) << 8) + (self.bit_reader.hold() >> 8)) % 31 + != 0 + { + self.mode = Mode::Bad; + return self.bad("incorrect header check\0"); + } - if self.wbits == 0 { - self.wbits = len; - } + if self.bit_reader.bits(4) != Z_DEFLATED as u64 { + self.mode = Mode::Bad; + return self.bad("unknown compression method\0"); + } - if len as i32 > MAX_WBITS || len > self.wbits { - self.mode = Mode::Bad; - return self.bad("invalid window size\0"); - } + self.bit_reader.drop_bits(4); + let len = self.bit_reader.bits(4) as u8 + 8; - self.dmax = 1 << len; - self.gzip_flags = 0; // indicate zlib header - self.checksum = crate::ADLER32_INITIAL_VALUE as _; + if self.wbits == 0 { + self.wbits = len; + } - if self.bit_reader.hold() & 0x200 != 0 { - self.bit_reader.init_bits(); + if len as i32 > MAX_WBITS || len > self.wbits { + self.mode = Mode::Bad; + return self.bad("invalid window size\0"); + } - self.mode = Mode::DictId; - self.dict_id() - } else { - self.bit_reader.init_bits(); + self.dmax = 1 << len; + self.gzip_flags = 0; // indicate zlib header + self.checksum = crate::ADLER32_INITIAL_VALUE as _; - self.mode = Mode::Type; - self.type_() - } - } + if self.bit_reader.hold() & 0x200 != 0 { + self.bit_reader.init_bits(); - fn flags(&mut self) -> ReturnCode { - need_bits!(self, 16); - self.gzip_flags = self.bit_reader.hold() as i32; + self.mode = Mode::DictId; - // Z_DEFLATED = 8 is the only supported method - if self.gzip_flags & 0xff != Z_DEFLATED { - self.mode = Mode::Bad; - return self.bad("unknown compression method\0"); - } + continue 'label; + } else { + self.bit_reader.init_bits(); - if self.gzip_flags & 0xe000 != 0 { - self.mode = Mode::Bad; - return self.bad("unknown header flags set\0"); - } + self.mode = Mode::Type; - if let Some(head) = self.head.as_mut() { - head.text = ((self.bit_reader.hold() >> 8) & 1) as i32; - } + continue 'label; + } + } + Mode::Flags => { + need_bits!(self, 16); + self.gzip_flags = self.bit_reader.hold() as i32; - if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let b0 = self.bit_reader.bits(8) as u8; - let b1 = (self.bit_reader.hold() >> 8) as u8; - self.checksum = crc32(self.checksum, &[b0, b1]); - } + // Z_DEFLATED = 8 is the only supported method + if self.gzip_flags & 0xff != Z_DEFLATED { + self.mode = Mode::Bad; + return self.bad("unknown compression method\0"); + } - self.bit_reader.init_bits(); - self.mode = Mode::Time; - self.time() - } + if self.gzip_flags & 0xe000 != 0 { + self.mode = Mode::Bad; + return self.bad("unknown header flags set\0"); + } - fn time(&mut self) -> ReturnCode { - need_bits!(self, 32); - if let Some(head) = self.head.as_mut() { - head.time = self.bit_reader.hold() as z_size; - } + if let Some(head) = self.head.as_mut() { + head.text = ((self.bit_reader.hold() >> 8) & 1) as i32; + } - if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let bytes = (self.bit_reader.hold() as u32).to_le_bytes(); - self.checksum = crc32(self.checksum, &bytes); - } + if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { + let b0 = self.bit_reader.bits(8) as u8; + let b1 = (self.bit_reader.hold() >> 8) as u8; + self.checksum = crc32(self.checksum, &[b0, b1]); + } - self.bit_reader.init_bits(); - self.mode = Mode::Os; - self.os() - } + self.bit_reader.init_bits(); + self.mode = Mode::Time; - fn os(&mut self) -> ReturnCode { - need_bits!(self, 16); - if let Some(head) = self.head.as_mut() { - head.xflags = (self.bit_reader.hold() & 0xff) as i32; - head.os = (self.bit_reader.hold() >> 8) as i32; - } + continue 'label; + } + Mode::Time => { + need_bits!(self, 32); + if let Some(head) = self.head.as_mut() { + head.time = self.bit_reader.hold() as z_size; + } - if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let bytes = (self.bit_reader.hold() as u16).to_le_bytes(); - self.checksum = crc32(self.checksum, &bytes); - } + if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { + let bytes = (self.bit_reader.hold() as u32).to_le_bytes(); + self.checksum = crc32(self.checksum, &bytes); + } - self.bit_reader.init_bits(); - self.mode = Mode::ExLen; - self.ex_len() - } + self.bit_reader.init_bits(); + self.mode = Mode::Os; - fn ex_len(&mut self) -> ReturnCode { - if (self.gzip_flags & 0x0400) != 0 { - need_bits!(self, 16); + continue 'label; + } + Mode::Os => { + need_bits!(self, 16); + if let Some(head) = self.head.as_mut() { + head.xflags = (self.bit_reader.hold() & 0xff) as i32; + head.os = (self.bit_reader.hold() >> 8) as i32; + } - // self.length (and head.extra_len) represent the length of the extra field - self.length = self.bit_reader.hold() as usize; - if let Some(head) = self.head.as_mut() { - head.extra_len = self.length as u32; - } + if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { + let bytes = (self.bit_reader.hold() as u16).to_le_bytes(); + self.checksum = crc32(self.checksum, &bytes); + } - if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let bytes = (self.bit_reader.hold() as u16).to_le_bytes(); - self.checksum = crc32(self.checksum, &bytes); - } - self.bit_reader.init_bits(); - } else if let Some(head) = self.head.as_mut() { - head.extra = core::ptr::null_mut(); - } + self.bit_reader.init_bits(); + self.mode = Mode::ExLen; - self.mode = Mode::Extra; - self.extra() - } + continue 'label; + } + Mode::ExLen => { + if (self.gzip_flags & 0x0400) != 0 { + need_bits!(self, 16); + + // self.length (and head.extra_len) represent the length of the extra field + self.length = self.bit_reader.hold() as usize; + if let Some(head) = self.head.as_mut() { + head.extra_len = self.length as u32; + } - fn extra(&mut self) -> ReturnCode { - if (self.gzip_flags & 0x0400) != 0 { - // self.length is the number of remaining `extra` bytes. But they may not all be available - let extra_available = Ord::min(self.length, self.bit_reader.bytes_remaining()); - - if extra_available > 0 { - if let Some(head) = self.head.as_mut() { - if !head.extra.is_null() { - // at `head.extra`, the caller has reserved `head.extra_max` bytes. - // in the deflated byte stream, we've found a gzip header with - // `head.extra_len` bytes of data. We must be careful because - // `head.extra_len` may be larger than `head.extra_max`. - - // how many bytes we've already written into `head.extra` - let written_so_far = head.extra_len as usize - self.length; - - // min of number of bytes available at dst and at src - let count = Ord::min( - (head.extra_max as usize).saturating_sub(written_so_far), - extra_available, - ); - - // location where we'll write: this saturates at the - // `head.extra.add(head.extra.max)` to prevent UB - let next_write_offset = Ord::min(written_so_far, head.extra_max as usize); - - unsafe { - core::ptr::copy_nonoverlapping( - self.bit_reader.as_mut_ptr(), - head.extra.add(next_write_offset), - count, - ); + if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { + let bytes = (self.bit_reader.hold() as u16).to_le_bytes(); + self.checksum = crc32(self.checksum, &bytes); } + self.bit_reader.init_bits(); + } else if let Some(head) = self.head.as_mut() { + head.extra = core::ptr::null_mut(); } - } - // Checksum - if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let extra_slice = &self.bit_reader.as_slice()[..extra_available]; - self.checksum = crc32(self.checksum, extra_slice) + self.mode = Mode::Extra; + + continue 'label; } + Mode::Extra => { + if (self.gzip_flags & 0x0400) != 0 { + // self.length is the number of remaining `extra` bytes. But they may not all be available + let extra_available = + Ord::min(self.length, self.bit_reader.bytes_remaining()); + + if extra_available > 0 { + if let Some(head) = self.head.as_mut() { + if !head.extra.is_null() { + // at `head.extra`, the caller has reserved `head.extra_max` bytes. + // in the deflated byte stream, we've found a gzip header with + // `head.extra_len` bytes of data. We must be careful because + // `head.extra_len` may be larger than `head.extra_max`. + + // how many bytes we've already written into `head.extra` + let written_so_far = head.extra_len as usize - self.length; + + // min of number of bytes available at dst and at src + let count = Ord::min( + (head.extra_max as usize).saturating_sub(written_so_far), + extra_available, + ); + + // location where we'll write: this saturates at the + // `head.extra.add(head.extra.max)` to prevent UB + let next_write_offset = + Ord::min(written_so_far, head.extra_max as usize); + + unsafe { + core::ptr::copy_nonoverlapping( + self.bit_reader.as_mut_ptr(), + head.extra.add(next_write_offset), + count, + ); + } + } + } - self.in_available -= extra_available; - self.bit_reader.advance(extra_available); - self.length -= extra_available; - } + // Checksum + if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { + let extra_slice = &self.bit_reader.as_slice()[..extra_available]; + self.checksum = crc32(self.checksum, extra_slice) + } - // Checks for errors occur after returning - if self.length != 0 { - return self.inflate_leave(ReturnCode::Ok); - } - } + self.in_available -= extra_available; + self.bit_reader.advance(extra_available); + self.length -= extra_available; + } - self.length = 0; - self.mode = Mode::Name; - self.name() - } + // Checks for errors occur after returning + if self.length != 0 { + return self.inflate_leave(ReturnCode::Ok); + } + } - fn name(&mut self) -> ReturnCode { - if (self.gzip_flags & 0x0800) != 0 { - if self.in_available == 0 { - return self.inflate_leave(ReturnCode::Ok); - } + self.length = 0; + self.mode = Mode::Name; - // the name string will always be null-terminated, but might be longer than we have - // space for in the header struct. Nonetheless, we read the whole thing. - let slice = self.bit_reader.as_slice(); - let null_terminator_index = slice.iter().position(|c| *c == 0); + continue 'label; + } + Mode::Name => { + if (self.gzip_flags & 0x0800) != 0 { + if self.in_available == 0 { + return self.inflate_leave(ReturnCode::Ok); + } - // we include the null terminator if it exists - let name_slice = match null_terminator_index { - Some(i) => &slice[..=i], - None => slice, - }; + // the name string will always be null-terminated, but might be longer than we have + // space for in the header struct. Nonetheless, we read the whole thing. + let slice = self.bit_reader.as_slice(); + let null_terminator_index = slice.iter().position(|c| *c == 0); + + // we include the null terminator if it exists + let name_slice = match null_terminator_index { + Some(i) => &slice[..=i], + None => slice, + }; + + // if the header has space, store as much as possible in there + if let Some(head) = self.head.as_mut() { + if !head.name.is_null() { + let remaining_name_bytes = + (head.name_max as usize).saturating_sub(self.length); + let copy = Ord::min(name_slice.len(), remaining_name_bytes); + + unsafe { + core::ptr::copy_nonoverlapping( + name_slice.as_ptr(), + head.name.add(self.length), + copy, + ) + }; + + self.length += copy; + } + } - // if the header has space, store as much as possible in there - if let Some(head) = self.head.as_mut() { - if !head.name.is_null() { - let remaining_name_bytes = (head.name_max as usize).saturating_sub(self.length); - let copy = Ord::min(name_slice.len(), remaining_name_bytes); - - unsafe { - core::ptr::copy_nonoverlapping( - name_slice.as_ptr(), - head.name.add(self.length), - copy, - ) - }; + if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { + self.checksum = crc32(self.checksum, name_slice); + } - self.length += copy; - } - } + let reached_end = name_slice.last() == Some(&0); + self.bit_reader.advance(name_slice.len()); - if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - self.checksum = crc32(self.checksum, name_slice); - } + if !reached_end && self.bit_reader.bytes_remaining() == 0 { + return self.inflate_leave(ReturnCode::Ok); + } + } else if let Some(head) = self.head.as_mut() { + head.name = core::ptr::null_mut(); + } - let reached_end = name_slice.last() == Some(&0); - self.bit_reader.advance(name_slice.len()); + self.length = 0; + self.mode = Mode::Comment; - if !reached_end && self.bit_reader.bytes_remaining() == 0 { - return self.inflate_leave(ReturnCode::Ok); - } - } else if let Some(head) = self.head.as_mut() { - head.name = core::ptr::null_mut(); - } + continue 'label; + } + Mode::Comment => { + if (self.gzip_flags & 0x01000) != 0 { + if self.in_available == 0 { + return self.inflate_leave(ReturnCode::Ok); + } - self.length = 0; - self.mode = Mode::Comment; - self.comment() - } + // the comment string will always be null-terminated, but might be longer than we have + // space for in the header struct. Nonetheless, we read the whole thing. + let slice = self.bit_reader.as_slice(); + let null_terminator_index = slice.iter().position(|c| *c == 0); + + // we include the null terminator if it exists + let comment_slice = match null_terminator_index { + Some(i) => &slice[..=i], + None => slice, + }; + + // if the header has space, store as much as possible in there + if let Some(head) = self.head.as_mut() { + if !head.comment.is_null() { + let remaining_comm_bytes = + (head.comm_max as usize).saturating_sub(self.length); + let copy = Ord::min(comment_slice.len(), remaining_comm_bytes); + unsafe { + core::ptr::copy_nonoverlapping( + comment_slice.as_ptr(), + head.comment.add(self.length), + copy, + ) + }; + + self.length += copy; + } + } - fn comment(&mut self) -> ReturnCode { - if (self.gzip_flags & 0x01000) != 0 { - if self.in_available == 0 { - return self.inflate_leave(ReturnCode::Ok); - } + if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { + self.checksum = crc32(self.checksum, comment_slice); + } - // the comment string will always be null-terminated, but might be longer than we have - // space for in the header struct. Nonetheless, we read the whole thing. - let slice = self.bit_reader.as_slice(); - let null_terminator_index = slice.iter().position(|c| *c == 0); + let reached_end = comment_slice.last() == Some(&0); + self.bit_reader.advance(comment_slice.len()); - // we include the null terminator if it exists - let comment_slice = match null_terminator_index { - Some(i) => &slice[..=i], - None => slice, - }; + if !reached_end && self.bit_reader.bytes_remaining() == 0 { + return self.inflate_leave(ReturnCode::Ok); + } + } else if let Some(head) = self.head.as_mut() { + head.comment = core::ptr::null_mut(); + } - // if the header has space, store as much as possible in there - if let Some(head) = self.head.as_mut() { - if !head.comment.is_null() { - let remaining_comm_bytes = (head.comm_max as usize).saturating_sub(self.length); - let copy = Ord::min(comment_slice.len(), remaining_comm_bytes); - unsafe { - core::ptr::copy_nonoverlapping( - comment_slice.as_ptr(), - head.comment.add(self.length), - copy, - ) - }; + self.mode = Mode::HCrc; - self.length += copy; + continue 'label; } - } - - if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - self.checksum = crc32(self.checksum, comment_slice); - } + Mode::HCrc => { + if (self.gzip_flags & 0x0200) != 0 { + need_bits!(self, 16); + + if (self.wrap & 4) != 0 + && self.bit_reader.hold() as u32 != (self.checksum & 0xffff) + { + self.mode = Mode::Bad; + return self.bad("header crc mismatch\0"); + } - let reached_end = comment_slice.last() == Some(&0); - self.bit_reader.advance(comment_slice.len()); + self.bit_reader.init_bits(); + } - if !reached_end && self.bit_reader.bytes_remaining() == 0 { - return self.inflate_leave(ReturnCode::Ok); - } - } else if let Some(head) = self.head.as_mut() { - head.comment = core::ptr::null_mut(); - } + if let Some(head) = self.head.as_mut() { + head.hcrc = (self.gzip_flags >> 9) & 1; + head.done = 1; + } - self.mode = Mode::HCrc; - self.hcrc() - } + // compute crc32 checksum if not in raw mode + if (self.wrap & 4 != 0) && self.gzip_flags != 0 { + self.crc_fold = Crc32Fold::new(); + self.checksum = crate::CRC32_INITIAL_VALUE; + } - fn hcrc(&mut self) -> ReturnCode { - if (self.gzip_flags & 0x0200) != 0 { - need_bits!(self, 16); + self.mode = Mode::Type; - if (self.wrap & 4) != 0 && self.bit_reader.hold() as u32 != (self.checksum & 0xffff) { - self.mode = Mode::Bad; - return self.bad("header crc mismatch\0"); - } + continue 'label; + } + Mode::Type => { + use InflateFlush::*; + + match self.flush { + Block | Trees => return ReturnCode::Ok, + NoFlush | SyncFlush | Finish => { + // NOTE: this is slightly different to what zlib-rs does! + self.mode = Mode::TypeDo; + continue 'label; + } + } + } + Mode::TypeDo => { + if self.flags.contains(Flags::IS_LAST_BLOCK) { + self.bit_reader.next_byte_boundary(); + self.mode = Mode::Check; - self.bit_reader.init_bits(); - } + continue 'label; + } - if let Some(head) = self.head.as_mut() { - head.hcrc = (self.gzip_flags >> 9) & 1; - head.done = 1; - } + need_bits!(self, 3); + // self.last = self.bit_reader.bits(1) != 0; + self.flags + .update(Flags::IS_LAST_BLOCK, self.bit_reader.bits(1) != 0); + self.bit_reader.drop_bits(1); - // compute crc32 checksum if not in raw mode - if (self.wrap & 4 != 0) && self.gzip_flags != 0 { - self.crc_fold = Crc32Fold::new(); - self.checksum = crate::CRC32_INITIAL_VALUE; - } + match self.bit_reader.bits(2) { + 0 => { + // eprintln!("inflate: stored block (last = {last})"); - self.mode = Mode::Type; - self.type_() - } + self.bit_reader.drop_bits(2); - fn sync(&mut self) -> ReturnCode { - ReturnCode::StreamError - } + self.mode = Mode::Stored; - #[inline(always)] - fn lit(&mut self) -> ReturnCode { - if self.writer.is_full() { - #[cfg(all(test, feature = "std"))] - eprintln!("Ok: writer is full ({} bytes)", self.writer.capacity()); - return self.inflate_leave(ReturnCode::Ok); - } + continue 'label; + } + 1 => { + // eprintln!("inflate: fixed codes block (last = {last})"); - self.writer.push(self.length as u8); + self.len_table = Table { + codes: Codes::Fixed, + bits: 9, + }; - self.mode = Mode::Len; + self.dist_table = Table { + codes: Codes::Fixed, + bits: 5, + }; - self.len() - } + self.mode = Mode::Len_; - fn check(&mut self) -> ReturnCode { - if !cfg!(feature = "__internal-fuzz-disable-checksum") && self.wrap != 0 { - need_bits!(self, 32); + self.bit_reader.drop_bits(2); - self.total += self.writer.len(); + if let InflateFlush::Trees = self.flush { + return self.inflate_leave(ReturnCode::Ok); + } else { + continue 'label; + } + } + 2 => { + // eprintln!("inflate: dynamic codes block (last = {last})"); - if self.wrap & 4 != 0 { - if self.gzip_flags != 0 { - self.crc_fold.fold(self.writer.filled(), self.checksum); - self.checksum = self.crc_fold.finish(); - } else { - self.checksum = adler32(self.checksum, self.writer.filled()); - } - } + self.bit_reader.drop_bits(2); - let given_checksum = if self.gzip_flags != 0 { - self.bit_reader.hold() as u32 - } else { - zswap32(self.bit_reader.hold() as u32) - }; + self.mode = Mode::Table; - self.out_available = self.writer.capacity() - self.writer.len(); + continue 'label; + } + 3 => { + // eprintln!("inflate: invalid block type"); - if self.wrap & 4 != 0 && given_checksum != self.checksum { - self.mode = Mode::Bad; - return self.bad("incorrect data check\0"); - } + self.bit_reader.drop_bits(2); - self.bit_reader.init_bits(); - } - self.mode = Mode::Length; - self.length() - } + self.mode = Mode::Bad; + return self.bad("invalid block type\0"); + } + _ => unsafe { core::hint::unreachable_unchecked() }, + } + } + Mode::Stored => { + self.bit_reader.next_byte_boundary(); - fn length(&mut self) -> ReturnCode { - // for gzip, last bytes contain LENGTH - if self.wrap != 0 && self.gzip_flags != 0 { - need_bits!(self, 32); - if (self.wrap & 4) != 0 && self.bit_reader.hold() != self.total as u64 { - self.mode = Mode::Bad; - return self.bad("incorrect length check\0"); - } + need_bits!(self, 32); - self.bit_reader.init_bits(); - } + let hold = self.bit_reader.bits(32) as u32; - // inflate stream terminated properly - self.inflate_leave(ReturnCode::StreamEnd) - } + // eprintln!("hold {hold:#x}"); - fn type_(&mut self) -> ReturnCode { - use InflateFlush::*; + if hold as u16 != !((hold >> 16) as u16) { + self.mode = Mode::Bad; + return self.bad("invalid stored block lengths\0"); + } - match self.flush { - Block | Trees => self.inflate_leave(ReturnCode::Ok), - NoFlush | SyncFlush | Finish => self.type_do(), - } - } + self.length = hold as usize & 0xFFFF; + // eprintln!("inflate: stored length {}", state.length); - fn type_do(&mut self) -> ReturnCode { - if self.flags.contains(Flags::IS_LAST_BLOCK) { - self.bit_reader.next_byte_boundary(); - self.mode = Mode::Check; - return self.check(); - } + self.bit_reader.init_bits(); - need_bits!(self, 3); - // self.last = self.bit_reader.bits(1) != 0; - self.flags - .update(Flags::IS_LAST_BLOCK, self.bit_reader.bits(1) != 0); - self.bit_reader.drop_bits(1); + if let InflateFlush::Trees = self.flush { + return self.inflate_leave(ReturnCode::Ok); + } else { + self.mode = Mode::CopyBlock; - match self.bit_reader.bits(2) { - 0 => { - // eprintln!("inflate: stored block (last = {last})"); + continue 'label; + } + } + Mode::CopyBlock => { + loop { + let mut copy = self.length; - self.bit_reader.drop_bits(2); + if copy == 0 { + break; + } - self.mode = Mode::Stored; - self.stored() - } - 1 => { - // eprintln!("inflate: fixed codes block (last = {last})"); + copy = Ord::min(copy, self.writer.remaining()); + copy = Ord::min(copy, self.bit_reader.bytes_remaining()); - self.len_table = Table { - codes: Codes::Fixed, - bits: 9, - }; + if copy == 0 { + return self.inflate_leave(ReturnCode::Ok); + } - self.dist_table = Table { - codes: Codes::Fixed, - bits: 5, - }; + self.writer.extend(&self.bit_reader.as_slice()[..copy]); + self.bit_reader.advance(copy); - self.mode = Mode::Len_; + self.length -= copy; + } - self.bit_reader.drop_bits(2); + self.mode = Mode::Type; - if let InflateFlush::Trees = self.flush { - self.inflate_leave(ReturnCode::Ok) - } else { - self.len_() + continue 'label; } - } - 2 => { - // eprintln!("inflate: dynamic codes block (last = {last})"); + Mode::Check => { + if !cfg!(feature = "__internal-fuzz-disable-checksum") && self.wrap != 0 { + need_bits!(self, 32); - self.bit_reader.drop_bits(2); - - self.mode = Mode::Table; - self.table() - } - 3 => { - // eprintln!("inflate: invalid block type"); + self.total += self.writer.len(); - self.bit_reader.drop_bits(2); + if self.wrap & 4 != 0 { + if self.gzip_flags != 0 { + self.crc_fold.fold(self.writer.filled(), self.checksum); + self.checksum = self.crc_fold.finish(); + } else { + self.checksum = adler32(self.checksum, self.writer.filled()); + } + } - self.mode = Mode::Bad; - self.bad("invalid block type\0") - } - _ => unsafe { core::hint::unreachable_unchecked() }, - } - } + let given_checksum = if self.gzip_flags != 0 { + self.bit_reader.hold() as u32 + } else { + zswap32(self.bit_reader.hold() as u32) + }; - fn stored(&mut self) -> ReturnCode { - self.bit_reader.next_byte_boundary(); + self.out_available = self.writer.capacity() - self.writer.len(); - need_bits!(self, 32); + if self.wrap & 4 != 0 && given_checksum != self.checksum { + self.mode = Mode::Bad; + return self.bad("incorrect data check\0"); + } - let hold = self.bit_reader.bits(32) as u32; + self.bit_reader.init_bits(); + } + self.mode = Mode::Length; - // eprintln!("hold {hold:#x}"); + continue 'label; + } + Mode::Len => { + let avail_in = self.bit_reader.bytes_remaining(); + let avail_out = self.writer.remaining(); + + // INFLATE_FAST_MIN_LEFT is important. It makes sure there is at least 32 bytes of free + // space available. This means for many SIMD operations we don't need to process a + // remainder; we just copy blindly, and a later operation will overwrite the extra copied + // bytes + if avail_in >= INFLATE_FAST_MIN_HAVE && avail_out >= INFLATE_FAST_MIN_LEFT { + inflate_fast_help(self, 0); + continue 'label; + } - if hold as u16 != !((hold >> 16) as u16) { - self.mode = Mode::Bad; - return self.bad("invalid stored block lengths\0"); - } + self.back = 0; - self.length = hold as usize & 0xFFFF; - // eprintln!("inflate: stored length {}", state.length); + // get a literal, length, or end-of-block code + let mut here; + loop { + let bits = self.bit_reader.bits(self.len_table.bits); + here = self.len_table_get(bits as usize); - self.bit_reader.init_bits(); + if here.bits <= self.bit_reader.bits_in_buffer() { + break; + } - if let InflateFlush::Trees = self.flush { - self.inflate_leave(ReturnCode::Ok) - } else { - self.mode = Mode::CopyBlock; - self.copy_block() - } - } + pull_byte!(self); + } - fn copy_block(&mut self) -> ReturnCode { - loop { - let mut copy = self.length; + if here.op != 0 && here.op & 0xf0 == 0 { + let last = here; + loop { + let bits = self.bit_reader.bits((last.bits + last.op) as usize) as u16; + here = self.len_table_get((last.val + (bits >> last.bits)) as usize); + if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { + break; + } - if copy == 0 { - break; - } + pull_byte!(self); + } - copy = Ord::min(copy, self.writer.remaining()); - copy = Ord::min(copy, self.bit_reader.bytes_remaining()); + self.bit_reader.drop_bits(last.bits); + self.back += last.bits as usize; + } - if copy == 0 { - return self.inflate_leave(ReturnCode::Ok); - } + self.bit_reader.drop_bits(here.bits); + self.back += here.bits as usize; + self.length = here.val as usize; - self.writer.extend(&self.bit_reader.as_slice()[..copy]); - self.bit_reader.advance(copy); + if here.op == 0 { + self.mode = Mode::Lit; - self.length -= copy; - } + continue 'label; + } else if here.op & 32 != 0 { + // end of block - self.mode = Mode::Type; - self.type_() - } + // eprintln!("inflate: end of block"); - fn len_(&mut self) -> ReturnCode { - self.mode = Mode::Len; - self.len() - } + self.back = usize::MAX; + self.mode = Mode::Type; - fn len(&mut self) -> ReturnCode { - let avail_in = self.bit_reader.bytes_remaining(); - let avail_out = self.writer.remaining(); + continue 'label; + } else if here.op & 64 != 0 { + self.mode = Mode::Bad; - // INFLATE_FAST_MIN_LEFT is important. It makes sure there is at least 32 bytes of free - // space available. This means for many SIMD operations we don't need to process a - // remainder; we just copy blindly, and a later operation will overwrite the extra copied - // bytes - if avail_in >= INFLATE_FAST_MIN_HAVE && avail_out >= INFLATE_FAST_MIN_LEFT { - return inflate_fast_help(self, 0); - } + return self.bad("invalid literal/length code\0"); + } else { + // length code + self.extra = (here.op & MAX_BITS) as usize; + self.mode = Mode::LenExt; - self.back = 0; + continue 'label; + } + } + Mode::Len_ => { + self.mode = Mode::Len; - // get a literal, length, or end-of-block code - let mut here; - loop { - let bits = self.bit_reader.bits(self.len_table.bits); - here = self.len_table_get(bits as usize); + continue 'label; + } + Mode::LenExt => { + let extra = self.extra; + + // get extra bits, if any + if extra != 0 { + need_bits!(self, extra); + self.length += self.bit_reader.bits(extra) as usize; + self.bit_reader.drop_bits(extra as u8); + self.back += extra; + } - if here.bits <= self.bit_reader.bits_in_buffer() { - break; - } + // eprintln!("inflate: length {}", state.length); - pull_byte!(self); - } + self.was = self.length; + self.mode = Mode::Dist; - if here.op != 0 && here.op & 0xf0 == 0 { - let last = here; - loop { - let bits = self.bit_reader.bits((last.bits + last.op) as usize) as u16; - here = self.len_table_get((last.val + (bits >> last.bits)) as usize); - if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { - break; + continue 'label; } + Mode::Lit => { + if self.writer.is_full() { + #[cfg(all(test, feature = "std"))] + eprintln!("Ok: writer is full ({} bytes)", self.writer.capacity()); + return self.inflate_leave(ReturnCode::Ok); + } - pull_byte!(self); - } + self.writer.push(self.length as u8); - self.bit_reader.drop_bits(last.bits); - self.back += last.bits as usize; - } + self.mode = Mode::Len; - self.bit_reader.drop_bits(here.bits); - self.back += here.bits as usize; - self.length = here.val as usize; + continue 'label; + } + Mode::Dist => { + // get distance code + let mut here; + loop { + let bits = self.bit_reader.bits(self.dist_table.bits) as usize; + here = self.dist_table_get(bits); + if here.bits <= self.bit_reader.bits_in_buffer() { + break; + } - if here.op == 0 { - self.mode = Mode::Lit; - self.lit() - } else if here.op & 32 != 0 { - // end of block - - // eprintln!("inflate: end of block"); - - self.back = usize::MAX; - self.mode = Mode::Type; - self.type_() - } else if here.op & 64 != 0 { - self.mode = Mode::Bad; - self.bad("invalid literal/length code\0") - } else { - // length code - self.extra = (here.op & MAX_BITS) as usize; - self.mode = Mode::LenExt; - self.len_ext() - } - } + pull_byte!(self); + } - fn len_ext(&mut self) -> ReturnCode { - let extra = self.extra; + if here.op & 0xf0 == 0 { + let last = here; - // get extra bits, if any - if extra != 0 { - need_bits!(self, extra); - self.length += self.bit_reader.bits(extra) as usize; - self.bit_reader.drop_bits(extra as u8); - self.back += extra; - } + loop { + let bits = self.bit_reader.bits((last.bits + last.op) as usize); + here = self + .dist_table_get(last.val as usize + ((bits as usize) >> last.bits)); - // eprintln!("inflate: length {}", state.length); + if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { + break; + } - self.was = self.length; - self.mode = Mode::Dist; - self.dist() - } + pull_byte!(self); + } - fn dist(&mut self) -> ReturnCode { - // get distance code - let mut here; - loop { - let bits = self.bit_reader.bits(self.dist_table.bits) as usize; - here = self.dist_table_get(bits); - if here.bits <= self.bit_reader.bits_in_buffer() { - break; - } + self.bit_reader.drop_bits(last.bits); + self.back += last.bits as usize; + } - pull_byte!(self); - } + self.bit_reader.drop_bits(here.bits); - if here.op & 0xf0 == 0 { - let last = here; + if here.op & 64 != 0 { + self.mode = Mode::Bad; + return self.bad("invalid distance code\0"); + } - loop { - let bits = self.bit_reader.bits((last.bits + last.op) as usize); - here = self.dist_table_get(last.val as usize + ((bits as usize) >> last.bits)); + self.offset = here.val as usize; - if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { - break; + self.extra = (here.op & MAX_BITS) as usize; + self.mode = Mode::DistExt; + + continue 'label; } + Mode::DistExt => { + let extra = self.extra; + + if extra > 0 { + need_bits!(self, extra); + self.offset += self.bit_reader.bits(extra) as usize; + self.bit_reader.drop_bits(extra as u8); + self.back += extra; + } - pull_byte!(self); - } + if INFLATE_STRICT && self.offset > self.dmax { + self.mode = Mode::Bad; + return self.bad("invalid distance code too far back\0"); + } - self.bit_reader.drop_bits(last.bits); - self.back += last.bits as usize; - } + // eprintln!("inflate: distance {}", state.offset); - self.bit_reader.drop_bits(here.bits); + self.mode = Mode::Match; - if here.op & 64 != 0 { - self.mode = Mode::Bad; - return self.bad("invalid distance code\0"); - } + continue 'label; + } + Mode::Match => { + 'match_: loop { + if self.writer.is_full() { + #[cfg(all(feature = "std", test))] + eprintln!( + "BufError: writer is full ({} bytes)", + self.writer.capacity() + ); + return self.inflate_leave(ReturnCode::Ok); + } - self.offset = here.val as usize; + let left = self.writer.remaining(); + let copy = self.writer.len(); - self.extra = (here.op & MAX_BITS) as usize; - self.mode = Mode::DistExt; - self.dist_ext() - } + let copy = if self.offset > copy { + // copy from window to output - fn dist_ext(&mut self) -> ReturnCode { - let extra = self.extra; + let mut copy = self.offset - copy; - if extra > 0 { - need_bits!(self, extra); - self.offset += self.bit_reader.bits(extra) as usize; - self.bit_reader.drop_bits(extra as u8); - self.back += extra; - } + if copy > self.window.have() { + if self.flags.contains(Flags::SANE) { + self.mode = Mode::Bad; + return self.bad("invalid distance too far back\0"); + } - if INFLATE_STRICT && self.offset > self.dmax { - self.mode = Mode::Bad; - return self.bad("invalid distance code too far back\0"); - } + // TODO INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + panic!("INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR") + } - // eprintln!("inflate: distance {}", state.offset); + let wnext = self.window.next(); + let wsize = self.window.size(); - self.mode = Mode::Match; - self.match_() - } + let from = if copy > wnext { + copy -= wnext; + wsize - copy + } else { + wnext - copy + }; - /// copy match from window to output - fn match_(&mut self) -> ReturnCode { - if self.writer.is_full() { - #[cfg(all(feature = "std", test))] - eprintln!( - "BufError: writer is full ({} bytes)", - self.writer.capacity() - ); - return self.inflate_leave(ReturnCode::Ok); - } + copy = Ord::min(copy, self.length); + copy = Ord::min(copy, left); + + self.writer + .extend_from_window(&self.window, from..from + copy); + + copy + } else { + let copy = Ord::min(self.length, left); + self.writer.copy_match(self.offset, copy); - let left = self.writer.remaining(); - let copy = self.writer.len(); + copy + }; - let copy = if self.offset > copy { - // copy from window to output + self.length -= copy; - let mut copy = self.offset - copy; + if self.length == 0 { + self.mode = Mode::Len; - if copy > self.window.have() { - if self.flags.contains(Flags::SANE) { - self.mode = Mode::Bad; - return self.bad("invalid distance too far back\0"); + continue 'label; + } else { + // otherwise it seems to recurse? + continue 'match_; + } + } } + Mode::Done => todo!(), + Mode::Table => { + need_bits!(self, 14); + self.nlen = self.bit_reader.bits(5) as usize + 257; + self.bit_reader.drop_bits(5); + self.ndist = self.bit_reader.bits(5) as usize + 1; + self.bit_reader.drop_bits(5); + self.ncode = self.bit_reader.bits(4) as usize + 4; + self.bit_reader.drop_bits(4); + + // TODO pkzit_bug_workaround + if self.nlen > 286 || self.ndist > 30 { + self.mode = Mode::Bad; + return self.bad("too many length or distance symbols\0"); + } - // TODO INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR - panic!("INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR") - } + self.have = 0; + self.mode = Mode::LenLens; - let wnext = self.window.next(); - let wsize = self.window.size(); + continue 'label; + } + Mode::LenLens => { + // permutation of code lengths ; + const ORDER: [u16; 19] = [ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, + ]; + + while self.have < self.ncode { + need_bits!(self, 3); + self.lens[ORDER[self.have] as usize] = self.bit_reader.bits(3) as u16; + self.have += 1; + self.bit_reader.drop_bits(3); + } - let from = if copy > wnext { - copy -= wnext; - wsize - copy - } else { - wnext - copy - }; + while self.have < 19 { + self.lens[ORDER[self.have] as usize] = 0; + self.have += 1; + } - copy = Ord::min(copy, self.length); - copy = Ord::min(copy, left); + self.len_table.bits = 7; - self.writer - .extend_from_window(&self.window, from..from + copy); + let InflateTable::Success(root) = inflate_table( + CodeType::Codes, + &self.lens, + 19, + &mut self.codes_codes, + self.len_table.bits, + &mut self.work, + ) else { + self.mode = Mode::Bad; + return self.bad("invalid code lengths set\0"); + }; - copy - } else { - let copy = Ord::min(self.length, left); - self.writer.copy_match(self.offset, copy); + self.len_table.codes = Codes::Codes; + self.len_table.bits = root; - copy - }; + self.have = 0; + self.mode = Mode::CodeLens; - self.length -= copy; + continue 'label; + } + Mode::CodeLens => { + while self.have < self.nlen + self.ndist { + let here = loop { + let bits = self.bit_reader.bits(self.len_table.bits); + let here = self.len_table_get(bits as usize); + if here.bits <= self.bit_reader.bits_in_buffer() { + break here; + } - if self.length == 0 { - self.mode = Mode::Len; - self.len() - } else { - // otherwise it seems to recurse? - self.match_() - } - } + pull_byte!(self); + }; - /// get dynamic table entries descriptor - - fn table(&mut self) -> ReturnCode { - need_bits!(self, 14); - self.nlen = self.bit_reader.bits(5) as usize + 257; - self.bit_reader.drop_bits(5); - self.ndist = self.bit_reader.bits(5) as usize + 1; - self.bit_reader.drop_bits(5); - self.ncode = self.bit_reader.bits(4) as usize + 4; - self.bit_reader.drop_bits(4); - - // TODO pkzit_bug_workaround - if self.nlen > 286 || self.ndist > 30 { - self.mode = Mode::Bad; - return self.bad("too many length or distance symbols\0"); - } + let here_bits = here.bits; - self.have = 0; - self.mode = Mode::LenLens; - self.len_lens() - } + match here.val { + 0..=15 => { + self.bit_reader.drop_bits(here_bits); + self.lens[self.have] = here.val; + self.have += 1; + } + 16 => { + need_bits!(self, here_bits as usize + 2); + self.bit_reader.drop_bits(here_bits); + if self.have == 0 { + self.mode = Mode::Bad; + return self.bad("invalid bit length repeat\0"); + } - /// get code length code lengths (not a typo) + let len = self.lens[self.have - 1]; + let copy = 3 + self.bit_reader.bits(2) as usize; + self.bit_reader.drop_bits(2); - fn len_lens(&mut self) -> ReturnCode { - // permutation of code lengths ; - const ORDER: [u16; 19] = [ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, - ]; + if self.have + copy > self.nlen + self.ndist { + self.mode = Mode::Bad; + return self.bad("invalid bit length repeat\0"); + } - while self.have < self.ncode { - need_bits!(self, 3); - self.lens[ORDER[self.have] as usize] = self.bit_reader.bits(3) as u16; - self.have += 1; - self.bit_reader.drop_bits(3); - } + for _ in 0..copy { + self.lens[self.have] = len; + self.have += 1; + } + } + 17 => { + need_bits!(self, here_bits as usize + 3); + self.bit_reader.drop_bits(here_bits); + let len = 0; + let copy = 3 + self.bit_reader.bits(3) as usize; + self.bit_reader.drop_bits(3); + + if self.have + copy > self.nlen + self.ndist { + self.mode = Mode::Bad; + return self.bad("invalid bit length repeat\0"); + } - while self.have < 19 { - self.lens[ORDER[self.have] as usize] = 0; - self.have += 1; - } + for _ in 0..copy { + self.lens[self.have] = len as u16; + self.have += 1; + } + } + 18.. => { + need_bits!(self, here_bits as usize + 7); + self.bit_reader.drop_bits(here_bits); + let len = 0; + let copy = 11 + self.bit_reader.bits(7) as usize; + self.bit_reader.drop_bits(7); + + if self.have + copy > self.nlen + self.ndist { + self.mode = Mode::Bad; + return self.bad("invalid bit length repeat\0"); + } - self.len_table.bits = 7; - - let InflateTable::Success(root) = inflate_table( - CodeType::Codes, - &self.lens, - 19, - &mut self.codes_codes, - self.len_table.bits, - &mut self.work, - ) else { - self.mode = Mode::Bad; - return self.bad("invalid code lengths set\0"); - }; + for _ in 0..copy { + self.lens[self.have] = len as u16; + self.have += 1; + } + } + } + } - self.len_table.codes = Codes::Codes; - self.len_table.bits = root; + // check for end-of-block code (better have one) + if self.lens[256] == 0 { + self.mode = Mode::Bad; + return self.bad("invalid code -- missing end-of-block\0"); + } - self.have = 0; - self.mode = Mode::CodeLens; - self.code_lens() - } + // build code tables - /// get length and distance code code lengths + self.len_table.bits = 10; - fn code_lens(&mut self) -> ReturnCode { - while self.have < self.nlen + self.ndist { - let here = loop { - let bits = self.bit_reader.bits(self.len_table.bits); - let here = self.len_table_get(bits as usize); - if here.bits <= self.bit_reader.bits_in_buffer() { - break here; - } + let InflateTable::Success(root) = inflate_table( + CodeType::Lens, + &self.lens, + self.nlen, + &mut self.len_codes, + self.len_table.bits, + &mut self.work, + ) else { + self.mode = Mode::Bad; + return self.bad("invalid literal/lengths set\0"); + }; - pull_byte!(self); - }; + self.len_table.codes = Codes::Len; + self.len_table.bits = root; - let here_bits = here.bits; + self.dist_table.bits = 9; - match here.val { - 0..=15 => { - self.bit_reader.drop_bits(here_bits); - self.lens[self.have] = here.val; - self.have += 1; - } - 16 => { - need_bits!(self, here_bits as usize + 2); - self.bit_reader.drop_bits(here_bits); - if self.have == 0 { + let InflateTable::Success(root) = inflate_table( + CodeType::Dists, + &self.lens[self.nlen..], + self.ndist, + &mut self.dist_codes, + self.dist_table.bits, + &mut self.work, + ) else { self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); - } + return self.bad("invalid distances set\0"); + }; - let len = self.lens[self.have - 1]; - let copy = 3 + self.bit_reader.bits(2) as usize; - self.bit_reader.drop_bits(2); + self.dist_table.bits = root; + self.dist_table.codes = Codes::Dist; - if self.have + copy > self.nlen + self.ndist { - self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); - } + self.mode = Mode::Len_; - for _ in 0..copy { - self.lens[self.have] = len; - self.have += 1; - } - } - 17 => { - need_bits!(self, here_bits as usize + 3); - self.bit_reader.drop_bits(here_bits); - let len = 0; - let copy = 3 + self.bit_reader.bits(3) as usize; - self.bit_reader.drop_bits(3); - - if self.have + copy > self.nlen + self.ndist { - self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); + if matches!(self.flush, InflateFlush::Trees) { + return self.inflate_leave(ReturnCode::Ok); } - for _ in 0..copy { - self.lens[self.have] = len as u16; - self.have += 1; - } + continue 'label; } - 18.. => { - need_bits!(self, here_bits as usize + 7); - self.bit_reader.drop_bits(here_bits); - let len = 0; - let copy = 11 + self.bit_reader.bits(7) as usize; - self.bit_reader.drop_bits(7); - - if self.have + copy > self.nlen + self.ndist { - self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); + Mode::Dict => { + if !self.flags.contains(Flags::HAVE_DICT) { + return self.inflate_leave(ReturnCode::NeedDict); } - for _ in 0..copy { - self.lens[self.have] = len as u16; - self.have += 1; - } - } - } - } - - // check for end-of-block code (better have one) - if self.lens[256] == 0 { - self.mode = Mode::Bad; - return self.bad("invalid code -- missing end-of-block\0"); - } - - // build code tables - - self.len_table.bits = 10; + self.checksum = crate::ADLER32_INITIAL_VALUE as _; - let InflateTable::Success(root) = inflate_table( - CodeType::Lens, - &self.lens, - self.nlen, - &mut self.len_codes, - self.len_table.bits, - &mut self.work, - ) else { - self.mode = Mode::Bad; - return self.bad("invalid literal/lengths set\0"); - }; - - self.len_table.codes = Codes::Len; - self.len_table.bits = root; - - self.dist_table.bits = 9; - - let InflateTable::Success(root) = inflate_table( - CodeType::Dists, - &self.lens[self.nlen..], - self.ndist, - &mut self.dist_codes, - self.dist_table.bits, - &mut self.work, - ) else { - self.mode = Mode::Bad; - return self.bad("invalid distances set\0"); - }; + self.mode = Mode::Type; - self.dist_table.bits = root; - self.dist_table.codes = Codes::Dist; - - self.mode = Mode::Len_; + continue 'label; + } + Mode::DictId => { + need_bits!(self, 32); - if matches!(self.flush, InflateFlush::Trees) { - return self.inflate_leave(ReturnCode::Ok); - } + self.checksum = zswap32(self.bit_reader.hold() as u32); - self.len_() - } + self.bit_reader.init_bits(); - fn dict_id(&mut self) -> ReturnCode { - need_bits!(self, 32); + self.mode = Mode::Dict; - self.checksum = zswap32(self.bit_reader.hold() as u32); + continue 'label; + } + Mode::Bad => { + let msg = "repeated call with bad state\0"; + #[cfg(all(feature = "std", test))] + dbg!(msg); + self.error_message = Some(msg); - self.bit_reader.init_bits(); + return ReturnCode::DataError; + } + Mode::Mem => { + return ReturnCode::MemError; + } + Mode::Sync => { + return ReturnCode::StreamError; + } + Mode::Length => { + // for gzip, last bytes contain LENGTH + if self.wrap != 0 && self.gzip_flags != 0 { + need_bits!(self, 32); + if (self.wrap & 4) != 0 && self.bit_reader.hold() != self.total as u64 { + self.mode = Mode::Bad; + return self.bad("incorrect length check\0"); + } - self.mode = Mode::Dict; - self.dict() - } + self.bit_reader.init_bits(); + } - fn dict(&mut self) -> ReturnCode { - if !self.flags.contains(Flags::HAVE_DICT) { - return self.inflate_leave(ReturnCode::NeedDict); + // inflate stream terminated properly + return ReturnCode::StreamEnd; + } + }; } - - self.checksum = crate::ADLER32_INITIAL_VALUE as _; - - self.mode = Mode::Type; - self.type_() - } - - fn mem(&mut self) -> ReturnCode { - self.inflate_leave(ReturnCode::MemError) } fn bad(&mut self, msg: &'static str) -> ReturnCode { @@ -1572,7 +1557,7 @@ impl<'a> State<'a> { } } -fn inflate_fast_help(state: &mut State, _start: usize) -> ReturnCode { +fn inflate_fast_help(state: &mut State, _start: usize) { let mut bit_reader = BitReader::new(&[]); core::mem::swap(&mut bit_reader, &mut state.bit_reader); @@ -1767,11 +1752,9 @@ fn inflate_fast_help(state: &mut State, _start: usize) -> ReturnCode { state.bit_reader = bit_reader; state.writer = writer; - match state.mode { - Mode::Type => state.type_(), - Mode::Len => state.len(), - Mode::Bad => state.bad(bad.unwrap()), - _ => unreachable!(), + if let Some(error_message) = bad { + debug_assert!(matches!(state.mode, Mode::Bad)); + state.bad(error_message); } } From 9e71c5a65cbac7b3659ef278f1642e1d4f866c1d Mon Sep 17 00:00:00 2001 From: Ameer Ghani Date: Thu, 31 Oct 2024 14:20:02 -0500 Subject: [PATCH 02/10] inflate: minor safety comments --- zlib-rs/src/inflate.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index ecc0310f..0a15bc0b 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -30,6 +30,8 @@ use self::{ const INFLATE_STRICT: bool = false; +// SAFETY: This struct must have the same layout as [`z_stream`], so that casts and transmutations +// between the two can work without UB. #[repr(C)] pub struct InflateStream<'a> { pub(crate) next_in: *mut crate::c_api::Bytef, @@ -59,6 +61,8 @@ pub unsafe fn set_mode_dict(strm: &mut z_stream) { } impl<'a> InflateStream<'a> { + // z_stream and DeflateStream must have the same layout. Do our best to check if this is true. + // (imperfect check, but should catch most mistakes.) const _S: () = assert!(core::mem::size_of::() == core::mem::size_of::()); const _A: () = assert!(core::mem::align_of::() == core::mem::align_of::()); @@ -130,6 +134,7 @@ pub fn uncompress_slice<'a>( input: &[u8], config: InflateConfig, ) -> (&'a mut [u8], ReturnCode) { + // SAFETY: [u8] is also a valid [MaybeUninit] let output_uninit = unsafe { core::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut MaybeUninit, output.len()) }; @@ -1875,6 +1880,7 @@ pub fn reset_with_config(stream: &mut InflateStream, config: InflateConfig) -> R let (ptr, len) = window.into_raw_parts(); assert_ne!(len, 0); + // SAFETY: window is discarded after this deallocation. unsafe { stream.alloc.deallocate(ptr, len) }; } @@ -2057,10 +2063,12 @@ pub fn sync(stream: &mut InflateStream) -> ReturnCode { } // search available input + // SAFETY: user guarantees that pointer and length are valid. let slice = unsafe { core::slice::from_raw_parts(stream.next_in, stream.avail_in as usize) }; let len; (state.have, len) = syncsearch(state.have, slice); + // SAFETY: syncsearch() returns an index that is in-bounds of the slice. stream.next_in = unsafe { stream.next_in.add(len) }; stream.avail_in -= len as u32; stream.total_in += len as z_size; @@ -2124,6 +2132,7 @@ pub unsafe fn copy<'a>( let state = &source.state; + // SAFETY: an initialized Writer is a valid MaybeUninit. let writer: MaybeUninit = unsafe { core::ptr::read(&state.writer as *const _ as *const MaybeUninit) }; @@ -2167,6 +2176,7 @@ pub unsafe fn copy<'a>( if !state.window.is_empty() { let Some(window) = state.window.clone_in(&source.alloc) else { + // SAFETY: state_allocation is not used again. source.alloc.deallocate(state_allocation, 1); return ReturnCode::MemError; }; From c5c85346a87187c62d357e0a29fcc036a68b7b7e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 10 Nov 2024 14:44:49 +0100 Subject: [PATCH 03/10] add test case for insufficient gzip header space --- test-libz-rs-sys/src/inflate.rs | 167 ++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/test-libz-rs-sys/src/inflate.rs b/test-libz-rs-sys/src/inflate.rs index 5725c3e2..005df65e 100644 --- a/test-libz-rs-sys/src/inflate.rs +++ b/test-libz-rs-sys/src/inflate.rs @@ -1202,6 +1202,173 @@ fn uncompress_edge_cases() { assert!(result.is_empty()); } +#[test] +fn gzip_header_fields_insufficient_space() { + let chunk_size = 512; + + let input = b"Hello World\n"; + + let extra = + "Scheduling and executing async tasks is a job handled by an async runtime, such as\0"; + let name = + "tokio, async-std, and smol. You’ve probably used them at some point, either directly or\0"; + let comment = + "indirectly. They, along with many frameworks that require async, do their best to hide\0"; + + let config = DeflateConfig { + window_bits: 31, + ..Default::default() + }; + + let mut stream = MaybeUninit::::zeroed(); + + const VERSION: *const c_char = libz_rs_sys::zlibVersion(); + const STREAM_SIZE: c_int = core::mem::size_of::() as c_int; + + let err = unsafe { + libz_rs_sys::deflateInit2_( + stream.as_mut_ptr(), + config.level, + config.method as i32, + config.window_bits, + config.mem_level, + config.strategy as i32, + VERSION, + STREAM_SIZE, + ) + }; + assert_eq!(err, 0); + + let stream = unsafe { stream.assume_init_mut() }; + + let mut header = libz_rs_sys::gz_header { + text: 0, + time: 0, + xflags: 0, + os: 0, + extra: extra.as_ptr() as *mut _, + extra_len: extra.len() as _, + extra_max: 0, + name: name.as_ptr() as *mut _, + name_max: 0, + comment: comment.as_ptr() as *mut _, + comm_max: 0, + hcrc: 1, + done: 0, + }; + + let err = unsafe { libz_rs_sys::deflateSetHeader(stream, &mut header) }; + assert_eq!(err, 0); + + let mut output_rs = [0u8; 512]; + stream.next_out = output_rs.as_mut_ptr(); + stream.avail_out = output_rs.len() as _; + + for chunk in input.chunks(chunk_size) { + stream.next_in = chunk.as_ptr() as *mut u8; + stream.avail_in = chunk.len() as _; + + let err = unsafe { deflate(stream, InflateFlush::NoFlush as _) }; + + assert_eq!(err, ReturnCode::Ok as i32, "{:?}", stream.msg); + } + + let err = unsafe { libz_rs_sys::deflate(stream, InflateFlush::Finish as _) }; + assert_eq!(ReturnCode::from(err), ReturnCode::StreamEnd); + + let output_rs = &mut output_rs[..stream.total_out as usize]; + + let err = unsafe { libz_rs_sys::deflateEnd(stream) }; + assert_eq!(ReturnCode::from(err), ReturnCode::Ok); + + assert_eq_rs_ng!({ + let mut stream = MaybeUninit::::zeroed(); + + const VERSION: *const c_char = libz_rs_sys::zlibVersion(); + const STREAM_SIZE: c_int = core::mem::size_of::() as c_int; + + let err = unsafe { + inflateInit2_( + stream.as_mut_ptr(), + config.window_bits, + VERSION, + STREAM_SIZE, + ) + }; + assert_eq!(err, 0); + + let stream = unsafe { stream.assume_init_mut() }; + + let mut output = [0u8; 64]; + stream.next_out = output.as_mut_ptr(); + stream.avail_out = output.len() as _; + + let mut extra_buf = [0u8; 64]; + assert!(extra_buf.len() < extra.len()); + let mut name_buf = [0u8; 64]; + assert!(name_buf.len() < name.len()); + let mut comment_buf = [0u8; 64]; + assert!(comment_buf.len() < comment.len()); + + let mut header = gz_header { + text: 0, + time: 0, + xflags: 0, + os: 0, + extra: extra_buf.as_mut_ptr(), + extra_len: 0, + extra_max: extra_buf.len() as _, + name: name_buf.as_mut_ptr(), + name_max: name_buf.len() as _, + comment: comment_buf.as_mut_ptr(), + comm_max: comment_buf.len() as _, + hcrc: 0, + done: 0, + }; + + let err = unsafe { inflateGetHeader(stream, &mut header) }; + assert_eq!(err, 0); + + for chunk in output_rs.chunks_mut(chunk_size) { + stream.next_in = chunk.as_mut_ptr(); + stream.avail_in = chunk.len() as _; + + let err = unsafe { inflate(stream, InflateFlush::NoFlush as _) }; + + if err == ReturnCode::StreamEnd as i32 { + break; + } + + assert_eq!(err, ReturnCode::Ok as i32); + } + + let err = unsafe { inflateEnd(stream) }; + assert_eq!(err, ReturnCode::Ok as i32); + + assert!(!header.extra.is_null()); + assert_eq!( + std::str::from_utf8(&extra_buf).unwrap(), + &extra[..extra_buf.len()] + ); + + assert!(!header.name.is_null()); + assert_eq!( + std::str::from_utf8(&name_buf).unwrap(), + &name[..name_buf.len()] + ); + + assert!(!header.comment.is_null()); + assert_eq!( + unsafe { CStr::from_ptr(comment_buf.as_ptr().cast()) } + .to_str() + .unwrap(), + &comment.trim_end_matches('\0')[..64] + ); + + (extra_buf, name_buf, comment_buf) + }); +} + #[test] fn gzip_chunked_1_byte() { gzip_chunked(1); From b7c36e1d623ed29d5c3c11a640e5fb38f7347b61 Mon Sep 17 00:00:00 2001 From: Ameer Ghani Date: Thu, 31 Oct 2024 14:20:32 -0500 Subject: [PATCH 04/10] inflate: more conservative bounds checking on gz header fields Paranoia: If self.length is miscalculated to be past {extra,name,comm}_max, it could result in us sending a pointer out of bounds when we do `ptr.add(self.length)`. Usually the number of bytes would be zero, but sending a pointer OOB with `ptr.add()` is automatic UB even if we don't actually count any bytes. We do have a guardrail against underflowing subtraction against self.length, but we should check and panic instead of saturating. Better safe than sorry. --- zlib-rs/src/inflate.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index 0a15bc0b..11e5ac5f 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -698,16 +698,21 @@ impl<'a> State<'a> { // min of number of bytes available at dst and at src let count = Ord::min( - (head.extra_max as usize).saturating_sub(written_so_far), + (head.extra_max as usize) + .checked_sub(written_so_far) + .expect("extra out of bounds"), extra_available, ); - // location where we'll write: this saturates at the + // SAFETY: location where we'll write: this saturates at the // `head.extra.add(head.extra.max)` to prevent UB let next_write_offset = Ord::min(written_so_far, head.extra_max as usize); unsafe { + // SAFETY: count is effectively bounded by head.extra_max + // and bit_reader.bytes_remaining(), so the count won't + // go out of bounds. core::ptr::copy_nonoverlapping( self.bit_reader.as_mut_ptr(), head.extra.add(next_write_offset), @@ -759,11 +764,14 @@ impl<'a> State<'a> { // if the header has space, store as much as possible in there if let Some(head) = self.head.as_mut() { if !head.name.is_null() { - let remaining_name_bytes = - (head.name_max as usize).saturating_sub(self.length); + let remaining_name_bytes = (head.name_max as usize) + .checked_sub(self.length) + .expect("name out of bounds"); let copy = Ord::min(name_slice.len(), remaining_name_bytes); unsafe { + // SAFETY: copy is effectively bound by the name length and + // head.name_max, so this won't go out of bounds. core::ptr::copy_nonoverlapping( name_slice.as_ptr(), head.name.add(self.length), @@ -814,10 +822,14 @@ impl<'a> State<'a> { // if the header has space, store as much as possible in there if let Some(head) = self.head.as_mut() { if !head.comment.is_null() { - let remaining_comm_bytes = - (head.comm_max as usize).saturating_sub(self.length); + let remaining_comm_bytes = (head.comm_max as usize) + .checked_sub(self.length) + .expect("comm out of bounds"); let copy = Ord::min(comment_slice.len(), remaining_comm_bytes); + unsafe { + // SAFETY: copy is effectively bound by the comment length and + // head.comm_max, so this won't go out of bounds. core::ptr::copy_nonoverlapping( comment_slice.as_ptr(), head.comment.add(self.length), From ede779244f63f7fcecdae8710b8ab8673d1bde3a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 10 Nov 2024 15:45:58 +0100 Subject: [PATCH 05/10] test specifically for extra not fitting --- test-libz-rs-sys/src/inflate.rs | 30 +++++++++++++++++++++--------- zlib-rs/src/inflate.rs | 4 +--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/test-libz-rs-sys/src/inflate.rs b/test-libz-rs-sys/src/inflate.rs index 005df65e..67a777ea 100644 --- a/test-libz-rs-sys/src/inflate.rs +++ b/test-libz-rs-sys/src/inflate.rs @@ -1204,14 +1204,14 @@ fn uncompress_edge_cases() { #[test] fn gzip_header_fields_insufficient_space() { - let chunk_size = 512; + let chunk_size = 16; let input = b"Hello World\n"; let extra = "Scheduling and executing async tasks is a job handled by an async runtime, such as\0"; let name = - "tokio, async-std, and smol. You’ve probably used them at some point, either directly or\0"; + "tokio, async-std, and smol. You've probably used them at some point, either directly or\0"; let comment = "indirectly. They, along with many frameworks that require async, do their best to hide\0"; @@ -1305,7 +1305,7 @@ fn gzip_header_fields_insufficient_space() { let mut extra_buf = [0u8; 64]; assert!(extra_buf.len() < extra.len()); - let mut name_buf = [0u8; 64]; + let mut name_buf = [0u8; 1]; assert!(name_buf.len() < name.len()); let mut comment_buf = [0u8; 64]; assert!(comment_buf.len() < comment.len()); @@ -1347,22 +1347,34 @@ fn gzip_header_fields_insufficient_space() { assert!(!header.extra.is_null()); assert_eq!( - std::str::from_utf8(&extra_buf).unwrap(), + if extra_buf.last() != Some(&0) { + std::str::from_utf8(&extra_buf).unwrap() + } else { + CStr::from_bytes_until_nul(&extra_buf) + .unwrap() + .to_str() + .unwrap() + }, &extra[..extra_buf.len()] ); assert!(!header.name.is_null()); assert_eq!( - std::str::from_utf8(&name_buf).unwrap(), + if name_buf.last() != Some(&0) { + std::str::from_utf8(&name_buf).unwrap() + } else { + CStr::from_bytes_until_nul(&name_buf) + .unwrap() + .to_str() + .unwrap() + }, &name[..name_buf.len()] ); assert!(!header.comment.is_null()); assert_eq!( - unsafe { CStr::from_ptr(comment_buf.as_ptr().cast()) } - .to_str() - .unwrap(), - &comment.trim_end_matches('\0')[..64] + std::str::from_utf8(&comment_buf).unwrap(), + &comment.trim_end_matches('\0')[..comment_buf.len()] ); (extra_buf, name_buf, comment_buf) diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index 11e5ac5f..7d3c5ca3 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -698,9 +698,7 @@ impl<'a> State<'a> { // min of number of bytes available at dst and at src let count = Ord::min( - (head.extra_max as usize) - .checked_sub(written_so_far) - .expect("extra out of bounds"), + (head.extra_max as usize).saturating_sub(written_so_far), extra_available, ); From 8664a7a1e3e5bc6d8693ad1d3ddca2adf6a59431 Mon Sep 17 00:00:00 2001 From: Ameer Ghani Date: Thu, 31 Oct 2024 14:26:45 -0500 Subject: [PATCH 06/10] inflate: safety comment around unchecked unreachable Add a safety comment around this unchecked unreachable statement, and convert the integer literals to binary literals to make it more obvious that the match statement is matching against bits. --- zlib-rs/src/inflate.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index 7d3c5ca3..2c2144f1 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -913,7 +913,7 @@ impl<'a> State<'a> { self.bit_reader.drop_bits(1); match self.bit_reader.bits(2) { - 0 => { + 0b00 => { // eprintln!("inflate: stored block (last = {last})"); self.bit_reader.drop_bits(2); @@ -922,7 +922,7 @@ impl<'a> State<'a> { continue 'label; } - 1 => { + 0b01 => { // eprintln!("inflate: fixed codes block (last = {last})"); self.len_table = Table { @@ -945,7 +945,7 @@ impl<'a> State<'a> { continue 'label; } } - 2 => { + 0b10 => { // eprintln!("inflate: dynamic codes block (last = {last})"); self.bit_reader.drop_bits(2); @@ -954,7 +954,7 @@ impl<'a> State<'a> { continue 'label; } - 3 => { + 0b11 => { // eprintln!("inflate: invalid block type"); self.bit_reader.drop_bits(2); @@ -962,7 +962,11 @@ impl<'a> State<'a> { self.mode = Mode::Bad; return self.bad("invalid block type\0"); } - _ => unsafe { core::hint::unreachable_unchecked() }, + _ => { + // SAFETY: bits(2) only yields a value of two bits, so this match is + // already exhaustive. + unsafe { core::hint::unreachable_unchecked() } + } } } Mode::Stored => { From 75efd27fbbc877d5e6d9e72c7bc045dc095f90db Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 10 Nov 2024 14:28:04 +0100 Subject: [PATCH 07/10] use safe `unreachable!` macro instead of `unreachable_unchecked` llvm is able to optimize the branch away anyway https://godbolt.org/z/x9srqoMjj so we can just have less unsafe code --- zlib-rs/src/inflate.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index 2c2144f1..702aa123 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -963,9 +963,8 @@ impl<'a> State<'a> { return self.bad("invalid block type\0"); } _ => { - // SAFETY: bits(2) only yields a value of two bits, so this match is - // already exhaustive. - unsafe { core::hint::unreachable_unchecked() } + // LLVM will optimize this branch away + unreachable!("BitReader::bits(2) only yields a value of two bits, so this match is already exhaustive") } } } From bb7281927746ded6dacba15f293108d62ca6c51f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 10 Nov 2024 14:34:47 +0100 Subject: [PATCH 08/10] `fn dispatch`: use `break` instead of `return` this does not yet cover the macros --- zlib-rs/src/inflate.rs | 80 +++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index 702aa123..d4c3ae20 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -555,12 +555,12 @@ impl<'a> State<'a> { != 0 { self.mode = Mode::Bad; - return self.bad("incorrect header check\0"); + break 'label self.bad("incorrect header check\0"); } if self.bit_reader.bits(4) != Z_DEFLATED as u64 { self.mode = Mode::Bad; - return self.bad("unknown compression method\0"); + break 'label self.bad("unknown compression method\0"); } self.bit_reader.drop_bits(4); @@ -572,7 +572,7 @@ impl<'a> State<'a> { if len as i32 > MAX_WBITS || len > self.wbits { self.mode = Mode::Bad; - return self.bad("invalid window size\0"); + break 'label self.bad("invalid window size\0"); } self.dmax = 1 << len; @@ -600,12 +600,12 @@ impl<'a> State<'a> { // Z_DEFLATED = 8 is the only supported method if self.gzip_flags & 0xff != Z_DEFLATED { self.mode = Mode::Bad; - return self.bad("unknown compression method\0"); + break 'label self.bad("unknown compression method\0"); } if self.gzip_flags & 0xe000 != 0 { self.mode = Mode::Bad; - return self.bad("unknown header flags set\0"); + break 'label self.bad("unknown header flags set\0"); } if let Some(head) = self.head.as_mut() { @@ -733,7 +733,7 @@ impl<'a> State<'a> { // Checks for errors occur after returning if self.length != 0 { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } } @@ -745,7 +745,7 @@ impl<'a> State<'a> { Mode::Name => { if (self.gzip_flags & 0x0800) != 0 { if self.in_available == 0 { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } // the name string will always be null-terminated, but might be longer than we have @@ -789,7 +789,7 @@ impl<'a> State<'a> { self.bit_reader.advance(name_slice.len()); if !reached_end && self.bit_reader.bytes_remaining() == 0 { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } } else if let Some(head) = self.head.as_mut() { head.name = core::ptr::null_mut(); @@ -803,7 +803,7 @@ impl<'a> State<'a> { Mode::Comment => { if (self.gzip_flags & 0x01000) != 0 { if self.in_available == 0 { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } // the comment string will always be null-terminated, but might be longer than we have @@ -847,7 +847,7 @@ impl<'a> State<'a> { self.bit_reader.advance(comment_slice.len()); if !reached_end && self.bit_reader.bytes_remaining() == 0 { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } } else if let Some(head) = self.head.as_mut() { head.comment = core::ptr::null_mut(); @@ -865,7 +865,7 @@ impl<'a> State<'a> { && self.bit_reader.hold() as u32 != (self.checksum & 0xffff) { self.mode = Mode::Bad; - return self.bad("header crc mismatch\0"); + break 'label self.bad("header crc mismatch\0"); } self.bit_reader.init_bits(); @@ -890,7 +890,7 @@ impl<'a> State<'a> { use InflateFlush::*; match self.flush { - Block | Trees => return ReturnCode::Ok, + Block | Trees => break 'label ReturnCode::Ok, NoFlush | SyncFlush | Finish => { // NOTE: this is slightly different to what zlib-rs does! self.mode = Mode::TypeDo; @@ -940,7 +940,7 @@ impl<'a> State<'a> { self.bit_reader.drop_bits(2); if let InflateFlush::Trees = self.flush { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } else { continue 'label; } @@ -960,7 +960,7 @@ impl<'a> State<'a> { self.bit_reader.drop_bits(2); self.mode = Mode::Bad; - return self.bad("invalid block type\0"); + break 'label self.bad("invalid block type\0"); } _ => { // LLVM will optimize this branch away @@ -979,7 +979,7 @@ impl<'a> State<'a> { if hold as u16 != !((hold >> 16) as u16) { self.mode = Mode::Bad; - return self.bad("invalid stored block lengths\0"); + break 'label self.bad("invalid stored block lengths\0"); } self.length = hold as usize & 0xFFFF; @@ -988,7 +988,7 @@ impl<'a> State<'a> { self.bit_reader.init_bits(); if let InflateFlush::Trees = self.flush { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } else { self.mode = Mode::CopyBlock; @@ -1007,7 +1007,7 @@ impl<'a> State<'a> { copy = Ord::min(copy, self.bit_reader.bytes_remaining()); if copy == 0 { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } self.writer.extend(&self.bit_reader.as_slice()[..copy]); @@ -1045,7 +1045,7 @@ impl<'a> State<'a> { if self.wrap & 4 != 0 && given_checksum != self.checksum { self.mode = Mode::Bad; - return self.bad("incorrect data check\0"); + break 'label self.bad("incorrect data check\0"); } self.bit_reader.init_bits(); @@ -1118,7 +1118,7 @@ impl<'a> State<'a> { } else if here.op & 64 != 0 { self.mode = Mode::Bad; - return self.bad("invalid literal/length code\0"); + break 'label self.bad("invalid literal/length code\0"); } else { // length code self.extra = (here.op & MAX_BITS) as usize; @@ -1154,7 +1154,7 @@ impl<'a> State<'a> { if self.writer.is_full() { #[cfg(all(test, feature = "std"))] eprintln!("Ok: writer is full ({} bytes)", self.writer.capacity()); - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } self.writer.push(self.length as u8); @@ -1199,7 +1199,7 @@ impl<'a> State<'a> { if here.op & 64 != 0 { self.mode = Mode::Bad; - return self.bad("invalid distance code\0"); + break 'label self.bad("invalid distance code\0"); } self.offset = here.val as usize; @@ -1221,7 +1221,7 @@ impl<'a> State<'a> { if INFLATE_STRICT && self.offset > self.dmax { self.mode = Mode::Bad; - return self.bad("invalid distance code too far back\0"); + break 'label self.bad("invalid distance code too far back\0"); } // eprintln!("inflate: distance {}", state.offset); @@ -1238,7 +1238,7 @@ impl<'a> State<'a> { "BufError: writer is full ({} bytes)", self.writer.capacity() ); - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } let left = self.writer.remaining(); @@ -1252,7 +1252,7 @@ impl<'a> State<'a> { if copy > self.window.have() { if self.flags.contains(Flags::SANE) { self.mode = Mode::Bad; - return self.bad("invalid distance too far back\0"); + break 'label self.bad("invalid distance too far back\0"); } // TODO INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR @@ -1308,7 +1308,7 @@ impl<'a> State<'a> { // TODO pkzit_bug_workaround if self.nlen > 286 || self.ndist > 30 { self.mode = Mode::Bad; - return self.bad("too many length or distance symbols\0"); + break 'label self.bad("too many length or distance symbols\0"); } self.have = 0; @@ -1345,7 +1345,7 @@ impl<'a> State<'a> { &mut self.work, ) else { self.mode = Mode::Bad; - return self.bad("invalid code lengths set\0"); + break 'label self.bad("invalid code lengths set\0"); }; self.len_table.codes = Codes::Codes; @@ -1381,7 +1381,7 @@ impl<'a> State<'a> { self.bit_reader.drop_bits(here_bits); if self.have == 0 { self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); + break 'label self.bad("invalid bit length repeat\0"); } let len = self.lens[self.have - 1]; @@ -1390,7 +1390,7 @@ impl<'a> State<'a> { if self.have + copy > self.nlen + self.ndist { self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); + break 'label self.bad("invalid bit length repeat\0"); } for _ in 0..copy { @@ -1407,7 +1407,7 @@ impl<'a> State<'a> { if self.have + copy > self.nlen + self.ndist { self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); + break 'label self.bad("invalid bit length repeat\0"); } for _ in 0..copy { @@ -1424,7 +1424,7 @@ impl<'a> State<'a> { if self.have + copy > self.nlen + self.ndist { self.mode = Mode::Bad; - return self.bad("invalid bit length repeat\0"); + break 'label self.bad("invalid bit length repeat\0"); } for _ in 0..copy { @@ -1438,7 +1438,7 @@ impl<'a> State<'a> { // check for end-of-block code (better have one) if self.lens[256] == 0 { self.mode = Mode::Bad; - return self.bad("invalid code -- missing end-of-block\0"); + break 'label self.bad("invalid code -- missing end-of-block\0"); } // build code tables @@ -1454,7 +1454,7 @@ impl<'a> State<'a> { &mut self.work, ) else { self.mode = Mode::Bad; - return self.bad("invalid literal/lengths set\0"); + break 'label self.bad("invalid literal/lengths set\0"); }; self.len_table.codes = Codes::Len; @@ -1471,7 +1471,7 @@ impl<'a> State<'a> { &mut self.work, ) else { self.mode = Mode::Bad; - return self.bad("invalid distances set\0"); + break 'label self.bad("invalid distances set\0"); }; self.dist_table.bits = root; @@ -1480,14 +1480,14 @@ impl<'a> State<'a> { self.mode = Mode::Len_; if matches!(self.flush, InflateFlush::Trees) { - return self.inflate_leave(ReturnCode::Ok); + break 'label self.inflate_leave(ReturnCode::Ok); } continue 'label; } Mode::Dict => { if !self.flags.contains(Flags::HAVE_DICT) { - return self.inflate_leave(ReturnCode::NeedDict); + break 'label self.inflate_leave(ReturnCode::NeedDict); } self.checksum = crate::ADLER32_INITIAL_VALUE as _; @@ -1513,13 +1513,13 @@ impl<'a> State<'a> { dbg!(msg); self.error_message = Some(msg); - return ReturnCode::DataError; + break 'label ReturnCode::DataError; } Mode::Mem => { - return ReturnCode::MemError; + break 'label ReturnCode::MemError; } Mode::Sync => { - return ReturnCode::StreamError; + break 'label ReturnCode::StreamError; } Mode::Length => { // for gzip, last bytes contain LENGTH @@ -1527,14 +1527,14 @@ impl<'a> State<'a> { need_bits!(self, 32); if (self.wrap & 4) != 0 && self.bit_reader.hold() != self.total as u64 { self.mode = Mode::Bad; - return self.bad("incorrect length check\0"); + break 'label self.bad("incorrect length check\0"); } self.bit_reader.init_bits(); } // inflate stream terminated properly - return ReturnCode::StreamEnd; + break 'label ReturnCode::StreamEnd; } }; } From d5a26dfc1162f9781595d0242328063ab3a531af Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 12 Nov 2024 20:11:40 +0100 Subject: [PATCH 09/10] load the inflate reader and writer to the stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Benchmark 2 (66 runs): target/release/examples/blogpost-uncompress rs-chunked 4 silesia-small.tar.gz measurement mean ± σ min … max outliers delta wall_time 76.8ms ± 2.42ms 75.6ms … 95.5ms 3 ( 5%) ⚡- 3.9% ± 1.1% peak_rss 24.1MB ± 73.9KB 23.9MB … 24.1MB 0 ( 0%) - 0.0% ± 0.1% cpu_cycles 308M ± 9.07M 305M … 378M 5 ( 8%) ⚡- 4.0% ± 1.0% instructions 941M ± 236 941M … 941M 0 ( 0%) ⚡- 2.3% ± 0.0% cache_references 3.08M ± 335K 2.78M … 5.42M 3 ( 5%) 💩+ 4.5% ± 3.2% cache_misses 126K ± 25.3K 87.0K … 263K 2 ( 3%) 💩+ 94.9% ± 13.4% branch_misses 4.08M ± 7.54K 4.07M … 4.13M 3 ( 5%) - 0.2% ± 0.1% --- zlib-rs/src/inflate.rs | 303 ++++++++++++++++++++++------------------- 1 file changed, 165 insertions(+), 138 deletions(-) diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index d4c3ae20..c6056bd8 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -490,24 +490,6 @@ impl<'a> State<'a> { } } -macro_rules! pull_byte { - ($self:expr) => { - match $self.bit_reader.pull_byte() { - Err(return_code) => return $self.inflate_leave(return_code), - Ok(_) => (), - } - }; -} - -macro_rules! need_bits { - ($self:expr, $n:expr) => { - match $self.bit_reader.need_bits($n) { - Err(return_code) => return $self.inflate_leave(return_code), - Ok(v) => v, - } - }; -} - // swaps endianness const fn zswap32(q: u32) -> u32 { u32::from_be(q.to_le()) @@ -517,8 +499,53 @@ const INFLATE_FAST_MIN_HAVE: usize = 15; const INFLATE_FAST_MIN_LEFT: usize = 260; impl<'a> State<'a> { + // NOTE: DO NOT RETURN FROM THIS FUNCTION! + // + // this function loads fields from `self` to the stack, and it is crucial that these fields + // are written back into `self` before exiting. + // + // Likewise, be careful with `self.foo()` function calls: the reader and writer are invalidated + // for the duration of this function! fn dispatch(&mut self) -> ReturnCode { - 'label: loop { + let mut writer; + let mut bit_reader; + + macro_rules! load { + ($state:expr) => { + bit_reader = core::mem::replace(&mut $state.bit_reader, BitReader::new(&[])); + writer = core::mem::replace(&mut $state.writer, Writer::new(&mut [])); + }; + } + + macro_rules! restore { + ($state:expr) => { + $state.bit_reader = bit_reader; + $state.writer = writer; + }; + } + + // load variables to the stack + load!(self); + + let return_code = 'label: loop { + macro_rules! pull_byte { + ($self:expr) => { + match bit_reader.pull_byte() { + Err(return_code) => break 'label return_code, + Ok(_) => (), + } + }; + } + + macro_rules! need_bits { + ($self:expr, $n:expr) => { + match bit_reader.need_bits($n) { + Err(return_code) => break 'label return_code, + Ok(v) => v, + } + }; + } + match self.mode { Mode::Head => { if self.wrap == 0 { @@ -530,15 +557,15 @@ impl<'a> State<'a> { need_bits!(self, 16); // Gzip - if (self.wrap & 2) != 0 && self.bit_reader.hold() == 0x8b1f { + if (self.wrap & 2) != 0 && bit_reader.hold() == 0x8b1f { if self.wbits == 0 { self.wbits = 15; } - let b0 = self.bit_reader.bits(8) as u8; - let b1 = (self.bit_reader.hold() >> 8) as u8; + let b0 = bit_reader.bits(8) as u8; + let b1 = (bit_reader.hold() >> 8) as u8; self.checksum = crc32(crate::CRC32_INITIAL_VALUE, &[b0, b1]); - self.bit_reader.init_bits(); + bit_reader.init_bits(); self.mode = Mode::Flags; @@ -551,20 +578,19 @@ impl<'a> State<'a> { // check if zlib header is allowed if (self.wrap & 1) == 0 - || ((self.bit_reader.bits(8) << 8) + (self.bit_reader.hold() >> 8)) % 31 - != 0 + || ((bit_reader.bits(8) << 8) + (bit_reader.hold() >> 8)) % 31 != 0 { self.mode = Mode::Bad; break 'label self.bad("incorrect header check\0"); } - if self.bit_reader.bits(4) != Z_DEFLATED as u64 { + if bit_reader.bits(4) != Z_DEFLATED as u64 { self.mode = Mode::Bad; break 'label self.bad("unknown compression method\0"); } - self.bit_reader.drop_bits(4); - let len = self.bit_reader.bits(4) as u8 + 8; + bit_reader.drop_bits(4); + let len = bit_reader.bits(4) as u8 + 8; if self.wbits == 0 { self.wbits = len; @@ -579,14 +605,14 @@ impl<'a> State<'a> { self.gzip_flags = 0; // indicate zlib header self.checksum = crate::ADLER32_INITIAL_VALUE as _; - if self.bit_reader.hold() & 0x200 != 0 { - self.bit_reader.init_bits(); + if bit_reader.hold() & 0x200 != 0 { + bit_reader.init_bits(); self.mode = Mode::DictId; continue 'label; } else { - self.bit_reader.init_bits(); + bit_reader.init_bits(); self.mode = Mode::Type; @@ -595,7 +621,7 @@ impl<'a> State<'a> { } Mode::Flags => { need_bits!(self, 16); - self.gzip_flags = self.bit_reader.hold() as i32; + self.gzip_flags = bit_reader.hold() as i32; // Z_DEFLATED = 8 is the only supported method if self.gzip_flags & 0xff != Z_DEFLATED { @@ -609,16 +635,16 @@ impl<'a> State<'a> { } if let Some(head) = self.head.as_mut() { - head.text = ((self.bit_reader.hold() >> 8) & 1) as i32; + head.text = ((bit_reader.hold() >> 8) & 1) as i32; } if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let b0 = self.bit_reader.bits(8) as u8; - let b1 = (self.bit_reader.hold() >> 8) as u8; + let b0 = bit_reader.bits(8) as u8; + let b1 = (bit_reader.hold() >> 8) as u8; self.checksum = crc32(self.checksum, &[b0, b1]); } - self.bit_reader.init_bits(); + bit_reader.init_bits(); self.mode = Mode::Time; continue 'label; @@ -626,15 +652,15 @@ impl<'a> State<'a> { Mode::Time => { need_bits!(self, 32); if let Some(head) = self.head.as_mut() { - head.time = self.bit_reader.hold() as z_size; + head.time = bit_reader.hold() as z_size; } if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let bytes = (self.bit_reader.hold() as u32).to_le_bytes(); + let bytes = (bit_reader.hold() as u32).to_le_bytes(); self.checksum = crc32(self.checksum, &bytes); } - self.bit_reader.init_bits(); + bit_reader.init_bits(); self.mode = Mode::Os; continue 'label; @@ -642,16 +668,16 @@ impl<'a> State<'a> { Mode::Os => { need_bits!(self, 16); if let Some(head) = self.head.as_mut() { - head.xflags = (self.bit_reader.hold() & 0xff) as i32; - head.os = (self.bit_reader.hold() >> 8) as i32; + head.xflags = (bit_reader.hold() & 0xff) as i32; + head.os = (bit_reader.hold() >> 8) as i32; } if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let bytes = (self.bit_reader.hold() as u16).to_le_bytes(); + let bytes = (bit_reader.hold() as u16).to_le_bytes(); self.checksum = crc32(self.checksum, &bytes); } - self.bit_reader.init_bits(); + bit_reader.init_bits(); self.mode = Mode::ExLen; continue 'label; @@ -661,16 +687,16 @@ impl<'a> State<'a> { need_bits!(self, 16); // self.length (and head.extra_len) represent the length of the extra field - self.length = self.bit_reader.hold() as usize; + self.length = bit_reader.hold() as usize; if let Some(head) = self.head.as_mut() { head.extra_len = self.length as u32; } if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let bytes = (self.bit_reader.hold() as u16).to_le_bytes(); + let bytes = (bit_reader.hold() as u16).to_le_bytes(); self.checksum = crc32(self.checksum, &bytes); } - self.bit_reader.init_bits(); + bit_reader.init_bits(); } else if let Some(head) = self.head.as_mut() { head.extra = core::ptr::null_mut(); } @@ -682,8 +708,7 @@ impl<'a> State<'a> { Mode::Extra => { if (self.gzip_flags & 0x0400) != 0 { // self.length is the number of remaining `extra` bytes. But they may not all be available - let extra_available = - Ord::min(self.length, self.bit_reader.bytes_remaining()); + let extra_available = Ord::min(self.length, bit_reader.bytes_remaining()); if extra_available > 0 { if let Some(head) = self.head.as_mut() { @@ -712,7 +737,7 @@ impl<'a> State<'a> { // and bit_reader.bytes_remaining(), so the count won't // go out of bounds. core::ptr::copy_nonoverlapping( - self.bit_reader.as_mut_ptr(), + bit_reader.as_mut_ptr(), head.extra.add(next_write_offset), count, ); @@ -722,12 +747,12 @@ impl<'a> State<'a> { // Checksum if (self.gzip_flags & 0x0200) != 0 && (self.wrap & 4) != 0 { - let extra_slice = &self.bit_reader.as_slice()[..extra_available]; + let extra_slice = &bit_reader.as_slice()[..extra_available]; self.checksum = crc32(self.checksum, extra_slice) } self.in_available -= extra_available; - self.bit_reader.advance(extra_available); + bit_reader.advance(extra_available); self.length -= extra_available; } @@ -750,7 +775,7 @@ impl<'a> State<'a> { // the name string will always be null-terminated, but might be longer than we have // space for in the header struct. Nonetheless, we read the whole thing. - let slice = self.bit_reader.as_slice(); + let slice = bit_reader.as_slice(); let null_terminator_index = slice.iter().position(|c| *c == 0); // we include the null terminator if it exists @@ -786,9 +811,9 @@ impl<'a> State<'a> { } let reached_end = name_slice.last() == Some(&0); - self.bit_reader.advance(name_slice.len()); + bit_reader.advance(name_slice.len()); - if !reached_end && self.bit_reader.bytes_remaining() == 0 { + if !reached_end && bit_reader.bytes_remaining() == 0 { break 'label self.inflate_leave(ReturnCode::Ok); } } else if let Some(head) = self.head.as_mut() { @@ -808,7 +833,7 @@ impl<'a> State<'a> { // the comment string will always be null-terminated, but might be longer than we have // space for in the header struct. Nonetheless, we read the whole thing. - let slice = self.bit_reader.as_slice(); + let slice = bit_reader.as_slice(); let null_terminator_index = slice.iter().position(|c| *c == 0); // we include the null terminator if it exists @@ -844,9 +869,9 @@ impl<'a> State<'a> { } let reached_end = comment_slice.last() == Some(&0); - self.bit_reader.advance(comment_slice.len()); + bit_reader.advance(comment_slice.len()); - if !reached_end && self.bit_reader.bytes_remaining() == 0 { + if !reached_end && bit_reader.bytes_remaining() == 0 { break 'label self.inflate_leave(ReturnCode::Ok); } } else if let Some(head) = self.head.as_mut() { @@ -862,13 +887,13 @@ impl<'a> State<'a> { need_bits!(self, 16); if (self.wrap & 4) != 0 - && self.bit_reader.hold() as u32 != (self.checksum & 0xffff) + && bit_reader.hold() as u32 != (self.checksum & 0xffff) { self.mode = Mode::Bad; break 'label self.bad("header crc mismatch\0"); } - self.bit_reader.init_bits(); + bit_reader.init_bits(); } if let Some(head) = self.head.as_mut() { @@ -900,23 +925,23 @@ impl<'a> State<'a> { } Mode::TypeDo => { if self.flags.contains(Flags::IS_LAST_BLOCK) { - self.bit_reader.next_byte_boundary(); + bit_reader.next_byte_boundary(); self.mode = Mode::Check; continue 'label; } need_bits!(self, 3); - // self.last = self.bit_reader.bits(1) != 0; + // self.last = bit_reader.bits(1) != 0; self.flags - .update(Flags::IS_LAST_BLOCK, self.bit_reader.bits(1) != 0); - self.bit_reader.drop_bits(1); + .update(Flags::IS_LAST_BLOCK, bit_reader.bits(1) != 0); + bit_reader.drop_bits(1); - match self.bit_reader.bits(2) { + match bit_reader.bits(2) { 0b00 => { // eprintln!("inflate: stored block (last = {last})"); - self.bit_reader.drop_bits(2); + bit_reader.drop_bits(2); self.mode = Mode::Stored; @@ -937,7 +962,7 @@ impl<'a> State<'a> { self.mode = Mode::Len_; - self.bit_reader.drop_bits(2); + bit_reader.drop_bits(2); if let InflateFlush::Trees = self.flush { break 'label self.inflate_leave(ReturnCode::Ok); @@ -948,7 +973,7 @@ impl<'a> State<'a> { 0b10 => { // eprintln!("inflate: dynamic codes block (last = {last})"); - self.bit_reader.drop_bits(2); + bit_reader.drop_bits(2); self.mode = Mode::Table; @@ -957,7 +982,7 @@ impl<'a> State<'a> { 0b11 => { // eprintln!("inflate: invalid block type"); - self.bit_reader.drop_bits(2); + bit_reader.drop_bits(2); self.mode = Mode::Bad; break 'label self.bad("invalid block type\0"); @@ -969,11 +994,11 @@ impl<'a> State<'a> { } } Mode::Stored => { - self.bit_reader.next_byte_boundary(); + bit_reader.next_byte_boundary(); need_bits!(self, 32); - let hold = self.bit_reader.bits(32) as u32; + let hold = bit_reader.bits(32) as u32; // eprintln!("hold {hold:#x}"); @@ -985,7 +1010,7 @@ impl<'a> State<'a> { self.length = hold as usize & 0xFFFF; // eprintln!("inflate: stored length {}", state.length); - self.bit_reader.init_bits(); + bit_reader.init_bits(); if let InflateFlush::Trees = self.flush { break 'label self.inflate_leave(ReturnCode::Ok); @@ -1003,15 +1028,15 @@ impl<'a> State<'a> { break; } - copy = Ord::min(copy, self.writer.remaining()); - copy = Ord::min(copy, self.bit_reader.bytes_remaining()); + copy = Ord::min(copy, writer.remaining()); + copy = Ord::min(copy, bit_reader.bytes_remaining()); if copy == 0 { break 'label self.inflate_leave(ReturnCode::Ok); } - self.writer.extend(&self.bit_reader.as_slice()[..copy]); - self.bit_reader.advance(copy); + writer.extend(&bit_reader.as_slice()[..copy]); + bit_reader.advance(copy); self.length -= copy; } @@ -1024,46 +1049,48 @@ impl<'a> State<'a> { if !cfg!(feature = "__internal-fuzz-disable-checksum") && self.wrap != 0 { need_bits!(self, 32); - self.total += self.writer.len(); + self.total += writer.len(); if self.wrap & 4 != 0 { if self.gzip_flags != 0 { - self.crc_fold.fold(self.writer.filled(), self.checksum); + self.crc_fold.fold(writer.filled(), self.checksum); self.checksum = self.crc_fold.finish(); } else { - self.checksum = adler32(self.checksum, self.writer.filled()); + self.checksum = adler32(self.checksum, writer.filled()); } } let given_checksum = if self.gzip_flags != 0 { - self.bit_reader.hold() as u32 + bit_reader.hold() as u32 } else { - zswap32(self.bit_reader.hold() as u32) + zswap32(bit_reader.hold() as u32) }; - self.out_available = self.writer.capacity() - self.writer.len(); + self.out_available = writer.capacity() - writer.len(); if self.wrap & 4 != 0 && given_checksum != self.checksum { self.mode = Mode::Bad; break 'label self.bad("incorrect data check\0"); } - self.bit_reader.init_bits(); + bit_reader.init_bits(); } self.mode = Mode::Length; continue 'label; } Mode::Len => { - let avail_in = self.bit_reader.bytes_remaining(); - let avail_out = self.writer.remaining(); + let avail_in = bit_reader.bytes_remaining(); + let avail_out = writer.remaining(); // INFLATE_FAST_MIN_LEFT is important. It makes sure there is at least 32 bytes of free // space available. This means for many SIMD operations we don't need to process a // remainder; we just copy blindly, and a later operation will overwrite the extra copied // bytes if avail_in >= INFLATE_FAST_MIN_HAVE && avail_out >= INFLATE_FAST_MIN_LEFT { + restore!(self); inflate_fast_help(self, 0); + load!(self); continue 'label; } @@ -1072,10 +1099,10 @@ impl<'a> State<'a> { // get a literal, length, or end-of-block code let mut here; loop { - let bits = self.bit_reader.bits(self.len_table.bits); + let bits = bit_reader.bits(self.len_table.bits); here = self.len_table_get(bits as usize); - if here.bits <= self.bit_reader.bits_in_buffer() { + if here.bits <= bit_reader.bits_in_buffer() { break; } @@ -1085,20 +1112,20 @@ impl<'a> State<'a> { if here.op != 0 && here.op & 0xf0 == 0 { let last = here; loop { - let bits = self.bit_reader.bits((last.bits + last.op) as usize) as u16; + let bits = bit_reader.bits((last.bits + last.op) as usize) as u16; here = self.len_table_get((last.val + (bits >> last.bits)) as usize); - if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { + if last.bits + here.bits <= bit_reader.bits_in_buffer() { break; } pull_byte!(self); } - self.bit_reader.drop_bits(last.bits); + bit_reader.drop_bits(last.bits); self.back += last.bits as usize; } - self.bit_reader.drop_bits(here.bits); + bit_reader.drop_bits(here.bits); self.back += here.bits as usize; self.length = here.val as usize; @@ -1138,8 +1165,8 @@ impl<'a> State<'a> { // get extra bits, if any if extra != 0 { need_bits!(self, extra); - self.length += self.bit_reader.bits(extra) as usize; - self.bit_reader.drop_bits(extra as u8); + self.length += bit_reader.bits(extra) as usize; + bit_reader.drop_bits(extra as u8); self.back += extra; } @@ -1151,13 +1178,13 @@ impl<'a> State<'a> { continue 'label; } Mode::Lit => { - if self.writer.is_full() { + if writer.is_full() { #[cfg(all(test, feature = "std"))] - eprintln!("Ok: writer is full ({} bytes)", self.writer.capacity()); + eprintln!("Ok: writer is full ({} bytes)", writer.capacity()); break 'label self.inflate_leave(ReturnCode::Ok); } - self.writer.push(self.length as u8); + writer.push(self.length as u8); self.mode = Mode::Len; @@ -1167,9 +1194,9 @@ impl<'a> State<'a> { // get distance code let mut here; loop { - let bits = self.bit_reader.bits(self.dist_table.bits) as usize; + let bits = bit_reader.bits(self.dist_table.bits) as usize; here = self.dist_table_get(bits); - if here.bits <= self.bit_reader.bits_in_buffer() { + if here.bits <= bit_reader.bits_in_buffer() { break; } @@ -1180,22 +1207,22 @@ impl<'a> State<'a> { let last = here; loop { - let bits = self.bit_reader.bits((last.bits + last.op) as usize); + let bits = bit_reader.bits((last.bits + last.op) as usize); here = self .dist_table_get(last.val as usize + ((bits as usize) >> last.bits)); - if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { + if last.bits + here.bits <= bit_reader.bits_in_buffer() { break; } pull_byte!(self); } - self.bit_reader.drop_bits(last.bits); + bit_reader.drop_bits(last.bits); self.back += last.bits as usize; } - self.bit_reader.drop_bits(here.bits); + bit_reader.drop_bits(here.bits); if here.op & 64 != 0 { self.mode = Mode::Bad; @@ -1214,8 +1241,8 @@ impl<'a> State<'a> { if extra > 0 { need_bits!(self, extra); - self.offset += self.bit_reader.bits(extra) as usize; - self.bit_reader.drop_bits(extra as u8); + self.offset += bit_reader.bits(extra) as usize; + bit_reader.drop_bits(extra as u8); self.back += extra; } @@ -1232,17 +1259,14 @@ impl<'a> State<'a> { } Mode::Match => { 'match_: loop { - if self.writer.is_full() { + if writer.is_full() { #[cfg(all(feature = "std", test))] - eprintln!( - "BufError: writer is full ({} bytes)", - self.writer.capacity() - ); + eprintln!("BufError: writer is full ({} bytes)", writer.capacity()); break 'label self.inflate_leave(ReturnCode::Ok); } - let left = self.writer.remaining(); - let copy = self.writer.len(); + let left = writer.remaining(); + let copy = writer.len(); let copy = if self.offset > copy { // copy from window to output @@ -1272,13 +1296,12 @@ impl<'a> State<'a> { copy = Ord::min(copy, self.length); copy = Ord::min(copy, left); - self.writer - .extend_from_window(&self.window, from..from + copy); + writer.extend_from_window(&self.window, from..from + copy); copy } else { let copy = Ord::min(self.length, left); - self.writer.copy_match(self.offset, copy); + writer.copy_match(self.offset, copy); copy }; @@ -1298,12 +1321,12 @@ impl<'a> State<'a> { Mode::Done => todo!(), Mode::Table => { need_bits!(self, 14); - self.nlen = self.bit_reader.bits(5) as usize + 257; - self.bit_reader.drop_bits(5); - self.ndist = self.bit_reader.bits(5) as usize + 1; - self.bit_reader.drop_bits(5); - self.ncode = self.bit_reader.bits(4) as usize + 4; - self.bit_reader.drop_bits(4); + self.nlen = bit_reader.bits(5) as usize + 257; + bit_reader.drop_bits(5); + self.ndist = bit_reader.bits(5) as usize + 1; + bit_reader.drop_bits(5); + self.ncode = bit_reader.bits(4) as usize + 4; + bit_reader.drop_bits(4); // TODO pkzit_bug_workaround if self.nlen > 286 || self.ndist > 30 { @@ -1324,9 +1347,9 @@ impl<'a> State<'a> { while self.have < self.ncode { need_bits!(self, 3); - self.lens[ORDER[self.have] as usize] = self.bit_reader.bits(3) as u16; + self.lens[ORDER[self.have] as usize] = bit_reader.bits(3) as u16; self.have += 1; - self.bit_reader.drop_bits(3); + bit_reader.drop_bits(3); } while self.have < 19 { @@ -1359,9 +1382,9 @@ impl<'a> State<'a> { Mode::CodeLens => { while self.have < self.nlen + self.ndist { let here = loop { - let bits = self.bit_reader.bits(self.len_table.bits); + let bits = bit_reader.bits(self.len_table.bits); let here = self.len_table_get(bits as usize); - if here.bits <= self.bit_reader.bits_in_buffer() { + if here.bits <= bit_reader.bits_in_buffer() { break here; } @@ -1372,21 +1395,21 @@ impl<'a> State<'a> { match here.val { 0..=15 => { - self.bit_reader.drop_bits(here_bits); + bit_reader.drop_bits(here_bits); self.lens[self.have] = here.val; self.have += 1; } 16 => { need_bits!(self, here_bits as usize + 2); - self.bit_reader.drop_bits(here_bits); + bit_reader.drop_bits(here_bits); if self.have == 0 { self.mode = Mode::Bad; break 'label self.bad("invalid bit length repeat\0"); } let len = self.lens[self.have - 1]; - let copy = 3 + self.bit_reader.bits(2) as usize; - self.bit_reader.drop_bits(2); + let copy = 3 + bit_reader.bits(2) as usize; + bit_reader.drop_bits(2); if self.have + copy > self.nlen + self.ndist { self.mode = Mode::Bad; @@ -1400,10 +1423,10 @@ impl<'a> State<'a> { } 17 => { need_bits!(self, here_bits as usize + 3); - self.bit_reader.drop_bits(here_bits); + bit_reader.drop_bits(here_bits); let len = 0; - let copy = 3 + self.bit_reader.bits(3) as usize; - self.bit_reader.drop_bits(3); + let copy = 3 + bit_reader.bits(3) as usize; + bit_reader.drop_bits(3); if self.have + copy > self.nlen + self.ndist { self.mode = Mode::Bad; @@ -1417,10 +1440,10 @@ impl<'a> State<'a> { } 18.. => { need_bits!(self, here_bits as usize + 7); - self.bit_reader.drop_bits(here_bits); + bit_reader.drop_bits(here_bits); let len = 0; - let copy = 11 + self.bit_reader.bits(7) as usize; - self.bit_reader.drop_bits(7); + let copy = 11 + bit_reader.bits(7) as usize; + bit_reader.drop_bits(7); if self.have + copy > self.nlen + self.ndist { self.mode = Mode::Bad; @@ -1499,9 +1522,9 @@ impl<'a> State<'a> { Mode::DictId => { need_bits!(self, 32); - self.checksum = zswap32(self.bit_reader.hold() as u32); + self.checksum = zswap32(bit_reader.hold() as u32); - self.bit_reader.init_bits(); + bit_reader.init_bits(); self.mode = Mode::Dict; @@ -1525,19 +1548,23 @@ impl<'a> State<'a> { // for gzip, last bytes contain LENGTH if self.wrap != 0 && self.gzip_flags != 0 { need_bits!(self, 32); - if (self.wrap & 4) != 0 && self.bit_reader.hold() != self.total as u64 { + if (self.wrap & 4) != 0 && bit_reader.hold() != self.total as u64 { self.mode = Mode::Bad; break 'label self.bad("incorrect length check\0"); } - self.bit_reader.init_bits(); + bit_reader.init_bits(); } // inflate stream terminated properly break 'label ReturnCode::StreamEnd; } }; - } + }; + + restore!(self); + + return_code } fn bad(&mut self, msg: &'static str) -> ReturnCode { From 697776815e8069d4954bd7dbb19a23d6a9c35e5f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 14 Nov 2024 11:52:42 +0100 Subject: [PATCH 10/10] clippy --- test-libz-rs-sys/src/inflate.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test-libz-rs-sys/src/inflate.rs b/test-libz-rs-sys/src/inflate.rs index 67a777ea..e44c797b 100644 --- a/test-libz-rs-sys/src/inflate.rs +++ b/test-libz-rs-sys/src/inflate.rs @@ -2160,10 +2160,10 @@ fn blow_up_the_stack_1() { let config = InflateConfig::default(); - let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, &INPUT, config); + let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, INPUT, config); assert_eq!(err, ReturnCode::DataError); - let (_, err) = uncompress_slice(&mut output_rs, &INPUT, config); + let (_, err) = uncompress_slice(&mut output_rs, INPUT, config); assert_eq!(err, ReturnCode::DataError); } @@ -2179,9 +2179,9 @@ fn blow_up_the_stack_2() { let config = InflateConfig::default(); - let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, &INPUT, config); + let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, INPUT, config); assert_eq!(err, ReturnCode::DataError); - let (_, err) = uncompress_slice(&mut output_rs, &INPUT, config); + let (_, err) = uncompress_slice(&mut output_rs, INPUT, config); assert_eq!(err, ReturnCode::DataError); }