Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gzip support for inflate #33

Merged
merged 49 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
132e34d
Started on gzip header parsing
bramtweedegolf Jan 18, 2024
0945ccc
Head
bramtweedegolf Feb 5, 2024
1ab0732
Merge branch 'main' of github.com:memorysafety/zlib-rs into gzip-func…
bramtweedegolf Feb 5, 2024
f80de1b
WIP gzip in inflate
bramtweedegolf Feb 7, 2024
86d1de5
WIP gzip header parsing
bramtweedegolf Feb 12, 2024
3115734
Merge branch 'main' into gzip-functions
bramtweedegolf Feb 12, 2024
d76ad62
Update
bramtweedegolf Feb 14, 2024
5577607
Merge branch 'main' of github.com:memorysafety/zlib-rs into gzip-func…
bramtweedegolf Feb 14, 2024
93bb3d6
Some refactors
bramtweedegolf Feb 14, 2024
09a1ec8
Inflate gzip header processing WIP
bramtweedegolf Feb 14, 2024
3b2f507
Finished parsing header, needs testing
bramtweedegolf Feb 15, 2024
cd25f39
Finished and tested extra info header parsing
bramtweedegolf Feb 16, 2024
d9c006f
Remove some testing code
bramtweedegolf Feb 19, 2024
3dd25fb
Clippy
bramtweedegolf Feb 19, 2024
aa5b2b7
Updating inflate
bramtweedegolf Feb 19, 2024
326ff1f
Merge branch 'main' of github.com:memorysafety/zlib-rs into gzip-func…
bramtweedegolf Feb 19, 2024
627ef66
Added CRC for Gzip, started on gzip inflate tests
bramtweedegolf Feb 19, 2024
4d82912
Merge branch 'gzip-functions' into gzip-inflate
bramtweedegolf Feb 19, 2024
6c55789
Clippy
bramtweedegolf Feb 19, 2024
5e72cc1
Removed some superfluous comments
bramtweedegolf Feb 19, 2024
ae1da9f
Updated tests
bramtweedegolf Feb 19, 2024
492d30d
Removed debug statement
bramtweedegolf Feb 19, 2024
fb2d06b
Fixed some gzip header crc issues
bramtweedegolf Feb 20, 2024
c5c651e
Small fixes
bramtweedegolf Feb 20, 2024
a7edd5a
Code cleanup
bramtweedegolf Feb 20, 2024
b8ff9e9
Merge branch 'main' of github.com:memorysafety/zlib-rs into gzip-inflate
bramtweedegolf Feb 20, 2024
7450ff9
Fixed small issues and formatting
bramtweedegolf Feb 20, 2024
38a1acc
Merge
bramtweedegolf Feb 20, 2024
f1aeb67
Merge branch 'main' of github.com:memorysafety/zlib-rs into gzip-inflate
bramtweedegolf Feb 20, 2024
36c75e2
Small fixes, merge
bramtweedegolf Feb 20, 2024
c700c6d
copy the state.head field, safely
folkertdev Feb 20, 2024
7315cea
prevent a dangling pointer in tests
folkertdev Feb 20, 2024
1bdc318
fix potential overflow in gzip header code
folkertdev Feb 20, 2024
ee56ea3
cleanup
folkertdev Feb 20, 2024
f84f162
remove unsafety
folkertdev Feb 20, 2024
cd09821
sanity test: crc of no input is the start crc
folkertdev Feb 20, 2024
4aa60dc
fix bug in name/comment decode
folkertdev Feb 20, 2024
5caaf65
fix stored block not updating the crc checksum
folkertdev Feb 20, 2024
4af011e
enable the fuzzer for gzip
folkertdev Feb 20, 2024
1b26048
test basic gzip header inflate
folkertdev Feb 20, 2024
ed9f429
fix bug in inflateGetHeader
folkertdev Feb 20, 2024
0bde1be
more interesting gzip test
folkertdev Feb 20, 2024
5117a6a
fix alignment bug in crc32
folkertdev Feb 20, 2024
5fa94da
self.in_available is out of date
folkertdev Feb 20, 2024
02ca800
Merge remote-tracking branch 'origin/main' into gzip-inflate
folkertdev Feb 21, 2024
3291499
read name/comment even if there is no space for the comment
folkertdev Feb 21, 2024
f530986
always read the name/comment, even if there is no space
folkertdev Feb 21, 2024
fa3d8f2
an attempt to clean up the extra field logic
folkertdev Feb 21, 2024
e8d815d
clippy
folkertdev Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/end_to_end.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::ffi::{c_char, c_int, c_uint};
fuzz_target!(|input: (String, DeflateConfig)| {
let (data, config) = input;

if !(0..=15).contains(&config.window_bits) {
if !(0..32).contains(&config.window_bits) {
// lower is raw, higher is gzip
return;
}
Expand Down
17 changes: 14 additions & 3 deletions libz-rs-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,20 @@ pub unsafe extern "C" fn inflateSetDictionary(
zlib_rs::inflate::set_dictionary(stream, dict) as _
}

// pub unsafe extern "C" fn inflateGetHeader(strm: z_streamp, head: gz_headerp) -> c_int {
// todo!("part of gzip support")
// }
// part of gzip
pub unsafe extern "C" fn inflateGetHeader(strm: z_streamp, head: gz_headerp) -> c_int {
if let Some(stream) = InflateStream::from_stream_mut(strm) {
let header = if head.is_null() {
None
} else {
Some(unsafe { &mut *(head) })
};

zlib_rs::inflate::get_header(stream, header) as i32
} else {
ReturnCode::StreamError as _
}
}

// undocumented but exposed function
pub unsafe extern "C" fn inflateUndermine(strm: *mut z_stream, subvert: i32) -> c_int {
Expand Down
231 changes: 223 additions & 8 deletions libz-rs-sys/src/tests/inflate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ use std::mem::ManuallyDrop;

use crate as libz_rs_sys;

use std::ffi::{c_char, c_int, c_void};
use std::ffi::{c_char, c_int, c_void, CStr};

use libz_rs_sys::*;
use zlib_rs::deflate::compress_slice;
use zlib_rs::inflate::{uncompress_slice, INFLATE_STATE_SIZE};
use zlib_rs::{Flush, MAX_WBITS};

const VERSION: *const c_char = "2.3.0\0".as_ptr() as *const c_char;
Expand Down Expand Up @@ -181,8 +183,31 @@ fn inf(input: &[u8], _what: &str, step: usize, win: i32, len: usize, err: c_int)

let mut out = vec![0u8; len];

let extra: [u8; 1024] = [0; 1024];
let name: [u8; 64] = [0; 64];
let comment: [u8; 64] = [0; 64];

// Set header
// See: https://www.zlib.net/manual.html
let mut header = gz_header {
text: 0,
time: 0,
xflags: 0,
os: 0,
extra: extra.as_ptr() as *mut u8,
extra_len: 0,
extra_max: 1024,
name: name.as_ptr() as *mut u8,
name_max: 64, // How / where should this be set?
comment: comment.as_ptr() as *mut u8,
comm_max: 64,
hcrc: 0,
done: 0,
};

if win == 47 {
todo!("gzip")
let err = unsafe { inflateGetHeader(&mut stream, &mut header) };
assert_eq!(ReturnCode::from(err), ReturnCode::Ok);
}

let mut have = input.len();
Expand All @@ -199,7 +224,9 @@ fn inf(input: &[u8], _what: &str, step: usize, win: i32, len: usize, err: c_int)
let ret = unsafe { inflate(&mut stream, Flush::NoFlush as _) };

if let Some(err) = err {
assert_eq!(ret, err)
if err != 9 {
assert_eq!(ret, err)
}
}

if !matches!(ret, Z_OK | Z_BUF_ERROR | Z_NEED_DICT) {
Expand Down Expand Up @@ -320,7 +347,7 @@ fn cover_wrap() {

// ------------------------------

let size = 2 * zlib_rs::inflate::INFLATE_STATE_SIZE + 256;
let size = 2 * INFLATE_STATE_SIZE + 256;
mem_limit(&mut strm, size);

ret = unsafe { inflatePrime(&mut strm, 16, 0) };
Expand Down Expand Up @@ -402,7 +429,6 @@ fn check_adler32() {
)
}
#[test]
#[ignore = "gzip"]
fn bad_header_crc() {
inf(
&[
Expand All @@ -417,7 +443,6 @@ fn bad_header_crc() {
}

#[test]
#[ignore = "gzip"]
fn check_gzip_length() {
inf(
&[
Expand All @@ -433,7 +458,6 @@ fn check_gzip_length() {
}

#[test]
#[ignore = "gzip"]
fn bad_zlib_header_check() {
inf(
&[0x78, 0x90],
Expand Down Expand Up @@ -814,7 +838,6 @@ fn copy_direct_from_output() {
}

#[test]
#[ignore = "gzip"]
fn cover_cve_2022_37434() {
inf(
&[
Expand Down Expand Up @@ -1070,3 +1093,195 @@ fn inflate_adler() {
let length = Ord::min(stream.total_out as usize, ORIGINAL.len());
assert_eq!(&uncompressed[..length], &ORIGINAL.as_bytes()[..length])
}

#[test]
fn inflate_get_header_non_gzip_stream() {
let mut stream = mem_setup();

let win = 15; // i.e. zlib compression (and not gzip)
let init_err = unsafe { inflateInit2_(&mut stream, win, VERSION, STREAM_SIZE) };
if init_err != Z_OK {
mem_done(&mut stream);
return;
}

let mut header = gz_header::default();

assert_eq!(
unsafe { inflateGetHeader(&mut stream, &mut header) },
ReturnCode::StreamError as i32
);
}

#[test]
fn inflate_window_bits_0_is_15() {
let input = b"Hello World!\n";

let mut compressed = [0; 64];
let (compressed, err) = compress_slice(&mut compressed, input, DeflateConfig::new(6));
assert_eq!(err, ReturnCode::Ok);

let config = InflateConfig { window_bits: 15 };
let mut output_15 = [0; 64];
let (output_15, err) = uncompress_slice(&mut output_15, compressed, config);
assert_eq!(err, ReturnCode::Ok);

let config = InflateConfig { window_bits: 0 };
let mut output_0 = [0; 64];
let (output_0, err) = uncompress_slice(&mut output_0, compressed, config);
assert_eq!(err, ReturnCode::Ok);

// for window size 0, the default of 15 is picked
// NOTE: the window size does not actually influence
// the output for an input this small.
assert_eq!(output_15, output_0);

assert_eq!(output_15, input);
}

#[test]
fn gzip_chunked() {
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::<libz_rs_sys::z_stream>::zeroed();

const VERSION: *const c_char = "2.1.4\0".as_ptr() as *const c_char;
const STREAM_SIZE: c_int = std::mem::size_of::<libz_rs_sys::z_stream>() 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);

stream.next_in = input.as_ptr() as *mut _;
stream.avail_in = input.len() as _;

let mut output_rs = [0u8; 512];
stream.next_out = output_rs.as_mut_ptr();
stream.avail_out = output_rs.len() as _;

let err = unsafe { libz_rs_sys::deflate(stream, Flush::Finish as _) };
assert_eq!(err, ReturnCode::StreamEnd as i32);

let output_rs = &mut output_rs[..stream.total_out as usize];

let err = unsafe { libz_rs_sys::deflateEnd(stream) };
assert_eq!(err, 0);

{
let mut stream = MaybeUninit::<libz_rs_sys::z_stream>::zeroed();

const VERSION: *const c_char = "2.1.4\0".as_ptr() as *const c_char;
const STREAM_SIZE: c_int = std::mem::size_of::<libz_rs_sys::z_stream>() as c_int;

let err = unsafe {
libz_rs_sys::inflateInit2_(
stream.as_mut_ptr(),
config.window_bits,
VERSION,
STREAM_SIZE,
)
};
assert_eq!(err, 0);

let stream = unsafe { stream.assume_init_mut() };

stream.next_in = output_rs.as_mut_ptr() as _;
stream.avail_in = output_rs.len() as _;

let mut output = [0u8; 64];
stream.next_out = output.as_mut_ptr();
stream.avail_out = output.len() as _;

let mut extra_buf = [0u8; 64];
let mut name_buf = [0u8; 64];
let mut comment_buf = [0u8; 256];

let mut header = libz_rs_sys::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 { libz_rs_sys::inflateGetHeader(stream, &mut header) };
assert_eq!(err, 0);

let err = unsafe { libz_rs_sys::inflate(stream, Flush::NoFlush as _) };
assert_eq!(err, ReturnCode::StreamEnd as i32);

let err = unsafe { libz_rs_sys::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')
);
}
}
2 changes: 1 addition & 1 deletion load-dynamic-libz-ng/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub unsafe fn compress(
f(dest, destLen, source, sourceLen)
}

unsafe fn deflate(strm: *mut libz_ng_sys::z_stream, flush: i32) -> std::ffi::c_int {
pub unsafe fn deflate(strm: *mut libz_ng_sys::z_stream, flush: i32) -> std::ffi::c_int {
const LIBZ_NG_SO: &str = "/home/folkertdev/rust/zlib-ng/libz-ng.so";

let lib = libloading::Library::new(LIBZ_NG_SO).unwrap();
Expand Down
20 changes: 20 additions & 0 deletions zlib-rs/src/c_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,26 @@ pub struct gz_header {
pub done: i32,
}

impl Default for gz_header {
fn default() -> Self {
Self {
text: 0,
time: 0,
xflags: 0,
os: 0,
extra: std::ptr::null_mut(),
extra_len: 0,
extra_max: 0,
name: std::ptr::null_mut(),
name_max: 0,
comment: std::ptr::null_mut(),
comm_max: 0,
hcrc: 0,
done: 0,
}
}
}

impl gz_header {
// based on the spec https://www.ietf.org/rfc/rfc1952.txt
//
Expand Down
Loading
Loading