Skip to content

Commit d3fba9b

Browse files
committed
Improve safety docs and pass MaybeUninit<u8> to RBrotliEncCompress.
1 parent bb9583e commit d3fba9b

File tree

6 files changed

+90
-36
lines changed

6 files changed

+90
-36
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ debug = true
1919
[workspace.lints.clippy]
2020
missing_safety_doc = "deny"
2121
undocumented_unsafe_blocks = "deny"
22+
23+
[workspace.lints.rust]
24+
unsafe_op_in_unsafe_fn = "deny"

bounded-utils/src/lib.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,23 @@ impl<T, const LOWER_BOUND: usize> BoundedSlice<T, LOWER_BOUND> {
183183
/// # Safety
184184
/// Caller must guarantee slice.len() >= LOWER_BOUND.
185185
pub unsafe fn from_slice_unchecked(slice: &[T]) -> &BoundedSlice<T, LOWER_BOUND> {
186-
// SAFETY: same layout and interpretation of metadata.
187-
&*(slice as *const [T] as *const Self)
186+
let ptr_to_self = slice as *const [T] as *const Self;
187+
// SAFETY: `Self` is a repr(transparent) wrapper around `[T]`, so it has the same memory
188+
// layout. Dereferencing the pointer is then valid, and the lifetimes in the function
189+
// signature guarantee that the returned slice does not outlive the input slice.
190+
unsafe { &*ptr_to_self }
188191
}
189192

190193
/// Constructs a new mutable BoundedSlice without checking that its length is sufficient.
191194
///
192195
/// # Safety
193196
/// Caller must guarantee slice.len() >= LOWER_BOUND.
194197
pub unsafe fn from_slice_unchecked_mut(slice: &mut [T]) -> &mut BoundedSlice<T, LOWER_BOUND> {
195-
// SAFETY: same layout and interpretation of metadata.
196-
&mut *(slice as *mut [T] as *mut Self)
198+
let ptr_to_self = slice as *mut [T] as *mut Self;
199+
// SAFETY: `Self` is a repr(transparent) wrapper around `[T]`, so it has the same memory
200+
// layout. Dereferencing the pointer is then valid, and the lifetimes in the function
201+
// signature guarantee that the returned slice does not outlive the input slice.
202+
unsafe { &mut *ptr_to_self }
197203
}
198204

199205
pub fn new_from_array<const ARR_SIZE: usize>(
@@ -406,7 +412,12 @@ macro_rules! impl_bounded_iterable {
406412

407413
#[inline(always)]
408414
unsafe fn internal_make(($([< state_ $bound:lower >]),*): Self::State) -> Self {
409-
($(BoundedUsize::new_unchecked([< state_ $bound:lower >])),*)
415+
// SAFETY: the safety invariant on `internal_make` guarantees that the `usize`
416+
// passed to `new_unchecked` is indeed bounded by the required bound, thanks to
417+
// the checks done in `iter` and `riter`.
418+
unsafe {
419+
($(BoundedUsize::new_unchecked([< state_ $bound:lower >])),*)
420+
}
410421
}
411422
}
412423
}

clib/rbrotli_enc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ RBrotliEncoder *RBrotliEncMakeEncoder(uint32_t quality);
2525
*
2626
* `*out_len` is overwritten with the total size of the encoded data.
2727
*
28+
* If this function returns `true`, the first `*out_len` bytes pointed at by `*out_data` will be
29+
* initialized.
30+
*
2831
* # Safety
2932
* `encoder` must be a valid Encoder created by RBrotliEncMakeEncoder that has not been
3033
* freed yet.

clib/src/lib.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
15+
use std::{
16+
mem::MaybeUninit,
17+
ptr::{slice_from_raw_parts, slice_from_raw_parts_mut},
18+
};
1619

1720
/// cbindgen:ignore
1821
type RBrotliEncoder = rbrotli_enc_lib::Encoder;
@@ -31,6 +34,9 @@ pub extern "C" fn RBrotliEncMakeEncoder(quality: u32) -> *mut RBrotliEncoder {
3134
///
3235
/// `*out_len` is overwritten with the total size of the encoded data.
3336
///
37+
/// If this function returns `true`, the first `*out_len` bytes pointed at by `*out_data` will be
38+
/// initialized.
39+
///
3440
/// # Safety
3541
/// `encoder` must be a valid Encoder created by RBrotliEncMakeEncoder that has not been
3642
/// freed yet.
@@ -44,23 +50,32 @@ pub unsafe extern "C" fn RBrotliEncCompress(
4450
encoder: *mut RBrotliEncoder,
4551
data: *const u8,
4652
len: usize,
47-
out_data: *mut *mut u8,
53+
out_data: *mut *mut MaybeUninit<u8>,
4854
out_len: *mut usize,
4955
) -> bool {
50-
let Some(encoder) = encoder.as_mut() else {
56+
// SAFETY: caller guarantees the pointer to be dereferenceable.
57+
let encoder = unsafe { encoder.as_mut() };
58+
let Some(encoder) = encoder else {
5159
return false;
5260
};
53-
let Some(data) = slice_from_raw_parts(data, len).as_ref() else {
61+
// SAFETY: caller guarantees `data` to point at `len` bytes of initialized memory.
62+
let data = unsafe { slice_from_raw_parts(data, len).as_ref() };
63+
let Some(data) = data else {
5464
return false;
5565
};
56-
let Some(out) = encoder.compress(
57-
data,
58-
slice_from_raw_parts_mut((*out_data).cast(), *out_len).as_mut(),
59-
) else {
66+
67+
// SAFETY: caller guarantees that `out_data` and `out_len` are dereferenceable and either:
68+
// - *out_data == ptr::null()
69+
// - *out_data points at a memory region at least `*out_len` bytes.
70+
let out_buf = unsafe { slice_from_raw_parts_mut(*out_data, *out_len).as_mut() };
71+
let Some(out) = encoder.compress(data, out_buf) else {
6072
return false;
6173
};
62-
out_data.write(out.as_ptr() as *mut u8);
63-
out_len.write(out.len());
74+
// SAFETY: caller guarantees that `out_data` and `out_len` are dereferenceable.
75+
unsafe {
76+
out_data.write(out.as_ptr() as *mut MaybeUninit<u8>);
77+
out_len.write(out.len());
78+
}
6479
true
6580
}
6681

@@ -75,7 +90,9 @@ pub unsafe extern "C" fn RBrotliEncMaxRequiredSize(
7590
encoder: *mut RBrotliEncoder,
7691
in_size: usize,
7792
) -> usize {
78-
let Some(encoder) = encoder.as_mut() else {
93+
// SAFETY: caller guarantees the pointer to be dereferenceable.
94+
let encoder = unsafe { encoder.as_mut() };
95+
let Some(encoder) = encoder else {
7996
return usize::MAX;
8097
};
8198
encoder.max_required_size(in_size)
@@ -88,7 +105,8 @@ pub unsafe extern "C" fn RBrotliEncMaxRequiredSize(
88105
/// freed yet.
89106
#[no_mangle]
90107
pub unsafe extern "C" fn RBrotliEncFreeEncoder(encoder: *mut RBrotliEncoder) {
91-
drop(Box::from_raw(encoder));
108+
// SAFETY: caller guarantees the pointer to be dereferenceable.
109+
drop(unsafe { Box::from_raw(encoder) });
92110
}
93111

94112
/// Returns true if this machine is supported by the encoder.

hugepage-buffer/src/lib.rs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,27 @@ unsafe fn allocate<T>(layout: Layout, zeroed: bool) -> *mut T {
4242
sysconf, MADV_HUGEPAGE, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_READ, PROT_WRITE,
4343
_SC_PAGE_SIZE,
4444
};
45-
let page_size = sysconf(_SC_PAGE_SIZE);
45+
// SAFETY: `sysconf` is always safe.
46+
let page_size = unsafe { sysconf(_SC_PAGE_SIZE) };
4647
assert!(page_size >= 0);
4748
let page_size = page_size as u64;
4849
const _: () = assert!(std::mem::size_of::<u64>() >= std::mem::size_of::<usize>());
4950
assert_eq!(page_size as u64 % layout.align() as u64, 0);
50-
let mem = libc::mmap(
51-
null_mut(),
52-
layout.size(),
53-
PROT_READ | PROT_WRITE,
54-
MAP_PRIVATE | MAP_ANONYMOUS,
55-
-1,
56-
0,
57-
);
58-
libc::madvise(mem, layout.size(), MADV_HUGEPAGE);
51+
// SAFETY: creating anonymous mappings is always safe.
52+
let mem = unsafe {
53+
libc::mmap(
54+
null_mut(),
55+
layout.size(),
56+
PROT_READ | PROT_WRITE,
57+
MAP_PRIVATE | MAP_ANONYMOUS,
58+
-1,
59+
0,
60+
)
61+
};
5962
assert_ne!(mem, MAP_FAILED);
60-
// SAFETY: mmap guarantees that the returned pointer is aligned to a page size and points
63+
// SAFETY: `madvise(MADV_HUGEPAGE)` is always safe.
64+
unsafe { libc::madvise(mem, layout.size(), MADV_HUGEPAGE) };
65+
// Safety note: mmap guarantees that the returned pointer is aligned to a page size and points
6166
// to an allocated region at least as long as its `len` argument. We check that the
6267
// required alignment is compatible with the page size before calling mmap.
6368
mem as *mut T
@@ -78,16 +83,23 @@ unsafe fn allocate<T>(layout: Layout, zeroed: bool) -> *mut T {
7883
///
7984
/// Safety:
8085
/// - `ptr` must have been allocated with `allocate`, with the same `layout` passed to this
81-
/// function.
86+
/// function, and not have been deallocated yet.
8287
/// - The memory pointed by `ptr` must still be valid.
8388
unsafe fn deallocate<T>(ptr: *mut T, layout: Layout) {
8489
#[cfg(all(target_os = "linux", not(miri)))]
8590
{
8691
use libc::c_void;
87-
libc::munmap(ptr as *mut c_void, layout.size());
92+
// SAFETY: `ptr` comes from a call to `mmap` with the same size as passed to this call to
93+
// `munmap`, and the memory was not unmapped before.
94+
unsafe {
95+
libc::munmap(ptr as *mut c_void, layout.size());
96+
}
8897
}
8998
#[cfg(any(not(target_os = "linux"), miri))]
90-
std::alloc::dealloc(ptr as *mut u8, layout)
99+
// SAFETY: the memory was allocated with `std::alloc::alloc` and not deallocated yet.
100+
unsafe {
101+
std::alloc::dealloc(ptr as *mut u8, layout)
102+
}
91103
}
92104

93105
impl<T: Copy, const LEN: usize> BoxedHugePageArray<T, LEN> {

lsb-bitwriter/src/lib.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,18 @@ impl<'a> BitWriter<'a> {
9999
self.bit_buffer |= bits << self.bits_in_buffer;
100100
let shift = 64 - self.bits_in_buffer;
101101
self.bits_in_buffer += count;
102-
copy_nonoverlapping(
103-
(&mut self.bit_buffer.to_le()) as *mut u64 as *mut u8,
104-
self.buf.as_mut_ptr().add(self.bytes_written).cast(),
105-
8,
106-
);
102+
// SAFETY: It is always safe to bit-copy `u8`s to `MaybeUninit<u8>`s. `src` points to an
103+
// 8-byte local buffer (as returned by `to_le_bytes`), and `dst` points to the buffer
104+
// provided by the user when creating the BitWriter, which for sure does not overlap it.
105+
// The caller guarantees at least `8` bytes are available in `self.buf` after
106+
// `self.bytes_written`.
107+
unsafe {
108+
copy_nonoverlapping(
109+
self.bit_buffer.to_le_bytes().as_ptr(),
110+
self.buf.as_mut_ptr().add(self.bytes_written).cast(),
111+
8,
112+
);
113+
}
107114
if self.bits_in_buffer >= 64 {
108115
self.bit_buffer = bits >> shift;
109116
self.bits_in_buffer -= 64;

0 commit comments

Comments
 (0)