Skip to content

Commit 5ecea73

Browse files
committed
Add safety comments to Writer implementation
1 parent 9538e0c commit 5ecea73

File tree

1 file changed

+50
-15
lines changed

1 file changed

+50
-15
lines changed

zlib-rs/src/inflate/writer.rs

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![allow(unsafe_op_in_unsafe_fn)] // FIXME
2-
31
use core::fmt;
42
use core::mem::MaybeUninit;
53
use core::ops::Range;
@@ -16,6 +14,18 @@ impl<'a> Writer<'a> {
1614
/// Creates a new `Writer` from a fully initialized buffer.
1715
#[inline]
1816
pub fn new(buf: &'a mut [u8]) -> Writer<'a> {
17+
// SAFETY: Because buf is a slice, most of the preconditions for
18+
// core::slice::from_raw_parts_mut are satisfied:
19+
// * buf.as_mut_ptr() is non-null.
20+
// * The memory range is within a single allocated object.
21+
// * buf is mutable, so the range is valid for both reads
22+
// and writes of up to buf.len() * size_of::<u8>() bytes.
23+
// * The contents of the slice are initialized.
24+
// * buf.as_mut_ptr() + buf.len() does not wrap around
25+
// the end of the address space.
26+
// The remaining precondition is enforced by the borrow checker when this function is called:
27+
// * The memory range cannot be accessed through any other pointer for the
28+
// duration of lifetime 'a.
1929
unsafe { Self::new_uninit(buf.as_mut_ptr(), buf.len()) }
2030
}
2131

@@ -26,6 +36,9 @@ impl<'a> Writer<'a> {
2636
/// The arguments must satisfy the requirements of [`core::slice::from_raw_parts_mut`].
2737
#[inline]
2838
pub unsafe fn new_uninit(ptr: *mut u8, len: usize) -> Writer<'a> {
39+
// SAFETY: The preconditions for WeakSliceMut::from_raw_parts_mut are the same
40+
// as for core::slice::from_raw_parts_mut, and the caller is responsible for
41+
// ensuring the latter.
2942
let buf = unsafe { WeakSliceMut::from_raw_parts_mut(ptr as *mut MaybeUninit<u8>, len) };
3043
Writer { buf, filled: 0 }
3144
}
@@ -272,27 +285,47 @@ impl<'a> Writer<'a> {
272285
/// # Safety
273286
///
274287
/// `src` must be safe to perform unaligned reads in `core::mem::size_of::<C>()` chunks until
275-
/// `end` is reached. `dst` must be safe to (unalingned) write that number of chunks.
288+
/// `end` is reached. `dst` must be safe to (unaligned) write that number of chunks.
276289
#[inline(always)]
277290
unsafe fn copy_chunk_unchecked<const N: usize>(
278291
mut src: *const MaybeUninit<u8>,
279292
mut dst: *mut MaybeUninit<u8>,
280293
length: usize,
281294
) {
282-
let end = src.add(length);
295+
if length == 0 {
296+
return;
297+
}
283298

284-
let chunk = load_chunk::<N>(src);
285-
store_chunk::<N>(dst, chunk);
299+
// SAFETY: The caller ensured that src + length is within (or just at the end of)
300+
// a readable range of bytes.
301+
// FIXME: The other safety precondition for `add` is that the amount being added
302+
// must fit in an `isize`. Should that be part of the documented safety preconditions
303+
// for this function, so that our caller is responsible for ensuring it?
304+
let end = unsafe { src.add(length) };
286305

287-
src = src.add(N);
288-
dst = dst.add(N);
306+
// SAFETY: We checked above that length != 0, so there is at least one chunk remaining.
307+
let chunk = unsafe { load_chunk::<N>(src) };
308+
unsafe { store_chunk::<N>(dst, chunk) };
289309

290-
while src < end {
291-
let chunk = load_chunk::<N>(src);
292-
store_chunk::<N>(dst, chunk);
310+
// SAFETY: src and dest haven't been modified yet, and we checked above that
311+
// length != 0, so adding one chunk (N bytes) to both src and dst will result in
312+
// a pointer in (or just at the end of) each of the underlying buffers.
313+
src = unsafe { src.add(N) };
314+
dst = unsafe { dst.add(N) };
293315

294-
src = src.add(N);
295-
dst = dst.add(N);
316+
while src < end {
317+
// SAFETY: The caller ensured that src and dst contain enough bytes to support
318+
// reads (from src) or writes (to dst) up to and including the chunk that contains
319+
// end. Note that, if length is not a multiple of N, we will copy up to N-1 bytes
320+
// past end.
321+
let chunk = unsafe { load_chunk::<N>(src) };
322+
unsafe { store_chunk::<N>(dst, chunk) };
323+
324+
// SAFETY: Because src is currently < end, we have at least one more chunk available
325+
// to copy, so there is room to advance the pointers by N within both the src and
326+
// dst bufs.
327+
src = unsafe { src.add(N) };
328+
dst = unsafe { dst.add(N) };
296329
}
297330
}
298331
}
@@ -302,15 +335,17 @@ impl<'a> Writer<'a> {
302335
/// Must be valid to read a `[u8; N]` value from `from` with an unaligned read.
303336
#[inline(always)]
304337
unsafe fn load_chunk<const N: usize>(from: *const MaybeUninit<u8>) -> [MaybeUninit<u8>; N] {
305-
core::ptr::read_unaligned(from.cast::<[MaybeUninit<u8>; N]>())
338+
// SAFETY: Checked by the caller.
339+
unsafe { core::ptr::read_unaligned(from.cast::<[MaybeUninit<u8>; N]>()) }
306340
}
307341

308342
/// # Safety
309343
///
310344
/// Must be valid to write a `[u8; N]` value to `out` with an unaligned write.
311345
#[inline(always)]
312346
unsafe fn store_chunk<const N: usize>(out: *mut MaybeUninit<u8>, chunk: [MaybeUninit<u8>; N]) {
313-
core::ptr::write_unaligned(out.cast(), chunk)
347+
// SAFETY: checked by the caller.
348+
unsafe { core::ptr::write_unaligned(out.cast(), chunk) }
314349
}
315350

316351
impl fmt::Debug for Writer<'_> {

0 commit comments

Comments
 (0)