Skip to content

Commit c5839ff

Browse files
Test: Verify overlapping buffers check for compress_into and decompress_into (#374)
This change updates `tests/security_overlap_test.rs` to include comprehensive test coverage for the `is_overlapping` check in `compress_into` and `decompress_into` methods. It covers: - Deflate, Zlib, and Gzip formats. - Compression and Decompression operations. - Various overlap scenarios: - Partial overlap at the beginning. - Partial overlap at the end. - One buffer strictly inside the other. - Exact match of buffers. - Edge case of touching but non-overlapping buffers. This ensures that the safety mechanism correctly rejects invalid usage that could lead to data corruption or undefined behavior, while allowing valid usage. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent a9034cb commit c5839ff

1 file changed

Lines changed: 118 additions & 25 deletions

File tree

tests/security_overlap_test.rs

Lines changed: 118 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,135 @@ mod tests {
77
fn test_compress_overlap() {
88
let mut compressor = Compressor::new(1).unwrap();
99
let mut buffer = vec![0u8; 1024];
10-
// Fill buffer with some data to compress
11-
for i in 0..512 {
12-
buffer[i] = (i % 256) as u8;
13-
}
1410

15-
// Create overlapping slices using unsafe
16-
let ptr = buffer.as_mut_ptr();
17-
let input_slice = unsafe { std::slice::from_raw_parts(ptr, 512) };
18-
let output_slice = unsafe { std::slice::from_raw_parts_mut(ptr.add(10), 512) };
11+
// Helper to test overlap for a specific format
12+
let mut check_overlap = |input_range: std::ops::Range<usize>, output_range: std::ops::Range<usize>, method: &str| {
13+
let ptr = buffer.as_mut_ptr();
14+
let input_slice = unsafe { std::slice::from_raw_parts(ptr.add(input_range.start), input_range.len()) };
15+
let output_slice = unsafe { std::slice::from_raw_parts_mut(ptr.add(output_range.start), output_range.len()) };
1916

20-
let res = compressor.compress_deflate_into(input_slice, output_slice);
21-
assert!(res.is_err());
22-
let err = res.unwrap_err();
23-
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
24-
assert_eq!(err.to_string(), "Input and output buffers overlap");
17+
let res = match method {
18+
"deflate" => compressor.compress_deflate_into(input_slice, output_slice),
19+
"zlib" => compressor.compress_zlib_into(input_slice, output_slice),
20+
"gzip" => compressor.compress_gzip_into(input_slice, output_slice),
21+
_ => panic!("Unknown method"),
22+
};
23+
24+
assert!(res.is_err(), "Expected error for overlap with method {}", method);
25+
let err = res.unwrap_err();
26+
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
27+
assert_eq!(err.to_string(), "Input and output buffers overlap");
28+
};
29+
30+
// Scenarios:
31+
// 1. Partial overlap (start)
32+
// Input: [0, 100), Output: [50, 150) -> Overlap [50, 100)
33+
check_overlap(0..100, 50..150, "deflate");
34+
check_overlap(0..100, 50..150, "zlib");
35+
check_overlap(0..100, 50..150, "gzip");
36+
37+
// 2. Partial overlap (end)
38+
// Input: [50, 150), Output: [0, 100) -> Overlap [50, 100)
39+
check_overlap(50..150, 0..100, "deflate");
40+
check_overlap(50..150, 0..100, "zlib");
41+
check_overlap(50..150, 0..100, "gzip");
42+
43+
// 3. Inclusion (input inside output)
44+
// Input: [50, 100), Output: [0, 150) -> Overlap [50, 100)
45+
check_overlap(50..100, 0..150, "deflate");
46+
check_overlap(50..100, 0..150, "zlib");
47+
check_overlap(50..100, 0..150, "gzip");
48+
49+
// 4. Inclusion (output inside input)
50+
// Input: [0, 150), Output: [50, 100) -> Overlap [50, 100)
51+
check_overlap(0..150, 50..100, "deflate");
52+
check_overlap(0..150, 50..100, "zlib");
53+
check_overlap(0..150, 50..100, "gzip");
54+
55+
// 5. Exact match
56+
// Input: [0, 100), Output: [0, 100) -> Overlap [0, 100)
57+
check_overlap(0..100, 0..100, "deflate");
58+
check_overlap(0..100, 0..100, "zlib");
59+
check_overlap(0..100, 0..100, "gzip");
2560
}
2661

2762
#[test]
2863
fn test_decompress_overlap() {
2964
let mut decompressor = Decompressor::new();
3065
let mut buffer = vec![0u8; 1024];
3166

32-
// We don't strictly need valid compressed data to test the overlap check,
33-
// because the check happens before decompression logic.
34-
// But to be safe against future reordering, we can put some dummy data.
67+
// Helper to test overlap for a specific format
68+
let mut check_overlap = |input_range: std::ops::Range<usize>, output_range: std::ops::Range<usize>, method: &str| {
69+
let ptr = buffer.as_mut_ptr();
70+
let input_slice = unsafe { std::slice::from_raw_parts(ptr.add(input_range.start), input_range.len()) };
71+
let output_slice = unsafe { std::slice::from_raw_parts_mut(ptr.add(output_range.start), output_range.len()) };
72+
73+
let res = match method {
74+
"deflate" => decompressor.decompress_deflate_into(input_slice, output_slice),
75+
"zlib" => decompressor.decompress_zlib_into(input_slice, output_slice),
76+
"gzip" => decompressor.decompress_gzip_into(input_slice, output_slice),
77+
_ => panic!("Unknown method"),
78+
};
79+
80+
assert!(res.is_err(), "Expected error for overlap with method {}", method);
81+
let err = res.unwrap_err();
82+
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
83+
assert_eq!(err.to_string(), "Input and output buffers overlap");
84+
};
85+
86+
// Scenarios:
87+
// 1. Partial overlap (start)
88+
check_overlap(0..100, 50..150, "deflate");
89+
check_overlap(0..100, 50..150, "zlib");
90+
check_overlap(0..100, 50..150, "gzip");
91+
92+
// 2. Partial overlap (end)
93+
check_overlap(50..150, 0..100, "deflate");
94+
check_overlap(50..150, 0..100, "zlib");
95+
check_overlap(50..150, 0..100, "gzip");
96+
97+
// 3. Inclusion (input inside output)
98+
check_overlap(50..100, 0..150, "deflate");
99+
check_overlap(50..100, 0..150, "zlib");
100+
check_overlap(50..100, 0..150, "gzip");
101+
102+
// 4. Inclusion (output inside input)
103+
check_overlap(0..150, 50..100, "deflate");
104+
check_overlap(0..150, 50..100, "zlib");
105+
check_overlap(0..150, 50..100, "gzip");
106+
107+
// 5. Exact match
108+
check_overlap(0..100, 0..100, "deflate");
109+
check_overlap(0..100, 0..100, "zlib");
110+
check_overlap(0..100, 0..100, "gzip");
111+
}
112+
113+
#[test]
114+
fn test_no_overlap() {
115+
let mut compressor = Compressor::new(1).unwrap();
116+
let mut buffer = vec![0u8; 1024];
35117

36118
let ptr = buffer.as_mut_ptr();
37-
// Input: 0..100
119+
120+
// Input: [0, 100)
121+
// Output: [100, 200) -> No overlap (Touching at 100)
38122
let input_slice = unsafe { std::slice::from_raw_parts(ptr, 100) };
39-
// Output: 50..150 (Overlap 50..100)
40-
let output_slice = unsafe { std::slice::from_raw_parts_mut(ptr.add(50), 100) };
41-
42-
let res = decompressor.decompress_deflate_into(input_slice, output_slice);
43-
assert!(res.is_err(), "Result should be Err, got {:?}", res);
44-
let err = res.unwrap_err();
45-
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
46-
assert_eq!(err.to_string(), "Input and output buffers overlap");
123+
let output_slice = unsafe { std::slice::from_raw_parts_mut(ptr.add(100), 100) };
124+
125+
let res = compressor.compress_deflate_into(input_slice, output_slice);
126+
if let Err(e) = res {
127+
assert_ne!(e.to_string(), "Input and output buffers overlap");
128+
}
129+
130+
// Touching on the other side
131+
// Input: [100, 200)
132+
// Output: [0, 100) -> No overlap (Touching at 100)
133+
let input_slice = unsafe { std::slice::from_raw_parts(ptr.add(100), 100) };
134+
let output_slice = unsafe { std::slice::from_raw_parts_mut(ptr, 100) };
135+
136+
let res = compressor.compress_deflate_into(input_slice, output_slice);
137+
if let Err(e) = res {
138+
assert_ne!(e.to_string(), "Input and output buffers overlap");
139+
}
47140
}
48141
}

0 commit comments

Comments
 (0)