Skip to content

Commit c8324e7

Browse files
committed
🔒 Fix uninitialized memory exposure via set_len
This commit addresses a security vulnerability where `Vec::set_len` was used to "initialize" buffers before data was actually written to them. This pattern can expose uninitialized memory if a panic occurs during the subsequent write operations, as safe code (like `Drop` implementations) could observe the uninitialized data. The fix involves: - Using `Vec::spare_capacity_mut()` to obtain a slice of `MaybeUninit<u8>` for compression/decompression operations. - Deferring the `Vec::set_len()` call until after the operation has successfully completed. - Ensuring buffers are `clear()`ed before use to maintain consistent state. - Replacing unsafe `set_len` with safe `resize(..., 0)` for internal buffers that require initialization. Affected areas: - `src/stream.rs`: `DeflateEncoder::flush_buffer` - `src/compress/mod.rs`: Parallel compression for large inputs and `dp_path` buffer. - `src/batch.rs`: `BatchDecompressor::decompress_batch`
1 parent 196744c commit c8324e7

3 files changed

Lines changed: 47 additions & 51 deletions

File tree

src/batch.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,18 @@ impl BatchDecompressor {
6363
.map_init(
6464
|| (Decompressor::new(), Vec::new()),
6565
|(decompressor, buffer), (&input, &max_size)| {
66+
buffer.clear();
6667
if buffer.capacity() < max_size {
67-
buffer.reserve(max_size.saturating_sub(buffer.len()));
68-
}
69-
unsafe {
70-
buffer.set_len(max_size);
68+
buffer.reserve(max_size);
7169
}
70+
let buf_uninit = &mut buffer.spare_capacity_mut()[..max_size];
7271

73-
let (res, _, size) = decompressor.decompress(input, buffer);
72+
let (res, _, size) = unsafe { decompressor.decompress_uninit(input, buf_uninit) };
73+
buffer.clear();
7474
if res == DecompressResult::Success {
75+
unsafe {
76+
buffer.set_len(size);
77+
}
7578
Some(buffer[..size].to_vec())
7679
} else {
7780
None

src/compress/mod.rs

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -683,28 +683,28 @@ impl Compressor {
683683
let mode = if is_last { flush_mode } else { FlushMode::Sync };
684684

685685
let bound = Self::deflate_compress_bound(chunk.len());
686+
buf.clear();
686687
if buf.capacity() < bound {
687-
buf.reserve(bound - buf.len());
688-
}
689-
unsafe {
690-
buf.set_len(bound);
688+
buf.reserve(bound);
691689
}
692690

693-
let buf_uninit = slice_as_uninit_mut(buf);
691+
let buf_uninit = &mut buf.spare_capacity_mut()[..bound];
694692

695693
let (res, size, _) = compressor.compress(chunk, buf_uninit, mode);
696694
if res == CompressResult::Success {
697695
unsafe {
698696
buf.set_len(size);
699697
}
700-
if size < buf.capacity() / 2 {
698+
let result = if size < buf.capacity() / 2 {
701699
Ok(buf.to_vec())
702700
} else {
703701
Ok(std::mem::replace(
704702
buf,
705703
Vec::with_capacity(chunk_size + chunk_size / 2),
706704
))
707-
}
705+
};
706+
buf.clear();
707+
result
708708
} else {
709709
Err(io::Error::other("Compression failed"))
710710
}
@@ -920,12 +920,7 @@ impl Compressor {
920920
self.dp_costs[0] = 0;
921921

922922
self.dp_path.clear();
923-
if self.dp_path.capacity() < processed + 1 {
924-
self.dp_path.reserve(processed + 1 - self.dp_path.len());
925-
}
926-
unsafe {
927-
self.dp_path.set_len(processed + 1);
928-
}
923+
self.dp_path.resize(processed + 1, 0);
929924

930925
mf.reset();
931926
let mut pos = 0;
@@ -1717,12 +1712,7 @@ impl Compressor {
17171712
self.dp_costs[0] = 0;
17181713

17191714
self.dp_path.clear();
1720-
if self.dp_path.capacity() < processed + 1 {
1721-
self.dp_path.reserve(processed + 1 - self.dp_path.len());
1722-
}
1723-
unsafe {
1724-
self.dp_path.set_len(processed + 1);
1725-
}
1715+
self.dp_path.resize(processed + 1, 0);
17261716

17271717
mf.reset();
17281718
let mut pos = 0;

src/stream.rs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,24 @@ impl<W: Write + Send> DeflateEncoder<W> {
6565
if !final_block {
6666
bound += 5;
6767
}
68-
if output.len() < bound {
69-
output
70-
.try_reserve(bound - output.len())
71-
.map_err(io::Error::other)?;
72-
unsafe {
73-
output.set_len(bound);
74-
}
75-
}
68+
69+
output.clear();
70+
output
71+
.try_reserve(bound)
72+
.map_err(io::Error::other)?;
7673

7774
let mode = if final_block {
7875
crate::compress::FlushMode::Finish
7976
} else {
8077
crate::compress::FlushMode::Sync
8178
};
82-
let out_uninit = crate::common::slice_as_uninit_mut(output);
79+
let out_uninit = &mut output.spare_capacity_mut()[..bound];
8380
let (res, size, _) = compressor.compress(chunk, out_uninit, mode);
81+
output.clear();
8482
if res == CompressResult::Success {
83+
unsafe {
84+
output.set_len(size);
85+
}
8586
if let Some(writer) = &mut self.writer {
8687
writer.write_all(&output[..size])?;
8788
}
@@ -99,23 +100,24 @@ impl<W: Write + Send> DeflateEncoder<W> {
99100
if !(final_block && i == num_chunks - 1) {
100101
bound += 5;
101102
}
102-
if output.len() < bound {
103-
output
104-
.try_reserve(bound - output.len())
105-
.map_err(io::Error::other)?;
106-
unsafe {
107-
output.set_len(bound);
108-
}
109-
}
103+
104+
output.clear();
105+
output
106+
.try_reserve(bound)
107+
.map_err(io::Error::other)?;
110108

111109
let mode = if final_block && i == num_chunks - 1 {
112110
crate::compress::FlushMode::Finish
113111
} else {
114112
crate::compress::FlushMode::Sync
115113
};
116-
let out_uninit = crate::common::slice_as_uninit_mut(output);
114+
let out_uninit = &mut output.spare_capacity_mut()[..bound];
117115
let (res, size, _) = compressor.compress(chunk, out_uninit, mode);
116+
output.clear();
118117
if res == CompressResult::Success {
118+
unsafe {
119+
output.set_len(size);
120+
}
119121
Ok(size)
120122
} else {
121123
Err(io::Error::other("Compression failed"))
@@ -144,23 +146,24 @@ impl<W: Write + Send> DeflateEncoder<W> {
144146
if !final_block {
145147
bound += 5;
146148
}
147-
if output.len() < bound {
148-
output
149-
.try_reserve(bound - output.len())
150-
.map_err(io::Error::other)?;
151-
unsafe {
152-
output.set_len(bound);
153-
}
154-
}
149+
150+
output.clear();
151+
output
152+
.try_reserve(bound)
153+
.map_err(io::Error::other)?;
155154

156155
let mode = if final_block {
157156
crate::compress::FlushMode::Finish
158157
} else {
159158
crate::compress::FlushMode::Sync
160159
};
161-
let out_uninit = crate::common::slice_as_uninit_mut(output);
160+
let out_uninit = &mut output.spare_capacity_mut()[..bound];
162161
let (res, size, _) = compressor.compress(&self.buffer, out_uninit, mode);
162+
output.clear();
163163
if res == CompressResult::Success {
164+
unsafe {
165+
output.set_len(size);
166+
}
164167
if let Some(writer) = &mut self.writer {
165168
writer.write_all(&output[..size])?;
166169
}

0 commit comments

Comments
 (0)