Skip to content

Commit be6f99a

Browse files
Add a soundness fix for array-init-cursor (#294)
1 parent 1cf18d1 commit be6f99a

File tree

5 files changed

+67
-14
lines changed

5 files changed

+67
-14
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ tui = "0.19.0"
4040
vec_map = "0.8.2"
4141

4242
# Our crates
43-
array-init-cursor = { version = "0.2.0", path = "crates/array-init-cursor" }
43+
array-init-cursor = { version = "0.2.1", path = "crates/array-init-cursor" }
4444
planus = { version = "1.1.1", path = "crates/planus", default-features = false }
4545
planus-buffer-inspection = { version = "1.1.1", path = "crates/planus-buffer-inspection" }
4646
planus-codegen = { version = "1.1.1", path = "crates/planus-codegen" }

crates/array-init-cursor/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "array-init-cursor"
3-
version = "0.2.0"
3+
version = "0.2.1"
44
edition.workspace = true
55
license.workspace = true
66
repository.workspace = true

crates/array-init-cursor/src/lib.rs

+63-9
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,7 @@ impl<'a, T, const N: usize> Cursor<'a, T, N> {
2525
}
2626

2727
fn write_impl(&mut self, value: [T; N]) {
28-
*self.slice = unsafe {
29-
let ptr = &value as *const [T; N] as *const [MaybeUninit<T>; N];
30-
let read_value = core::ptr::read(ptr);
31-
core::mem::drop(value);
32-
read_value
33-
};
28+
*self.slice = value.map(|v| MaybeUninit::new(v));
3429
}
3530

3631
/// Finishes the buffer by writing the remaining values.
@@ -53,8 +48,8 @@ impl<'a, T, const N: usize> Cursor<'a, T, N> {
5348
r
5449
}
5550

56-
unsafe fn into_buf(self) -> &'a mut [MaybeUninit<T>; N] {
57-
core::mem::transmute(self)
51+
fn into_buf(self) -> &'a mut [MaybeUninit<T>; N] {
52+
unsafe { core::mem::transmute(self) }
5853
}
5954

6055
/// Splits the cursor in two.
@@ -63,7 +58,7 @@ impl<'a, T, const N: usize> Cursor<'a, T, N> {
6358
/// `cargo check`, since the error is not discovered by `rustc` until it tries to instantiate
6459
/// the code.
6560
pub fn split<const L: usize, const R: usize>(self) -> (Cursor<'a, T, L>, Cursor<'a, T, R>) {
66-
let buf = unsafe { self.into_buf() };
61+
let buf = self.into_buf();
6762
let (l, r) = crate::util::split_mut::<_, N, L, R>(buf);
6863
(Cursor { slice: l }, Cursor { slice: r })
6964
}
@@ -94,3 +89,62 @@ impl<T, const N: usize> Drop for Cursor<'_, T, N> {
9489
}
9590
}
9691
}
92+
93+
#[cfg(test)]
94+
mod tests {
95+
use core::sync::atomic::AtomicU8;
96+
97+
use super::*;
98+
99+
#[test]
100+
fn test_drop() {
101+
struct DropCounter<'a>(&'a AtomicU8);
102+
impl core::ops::Drop for DropCounter<'_> {
103+
fn drop(&mut self) {
104+
self.0.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
105+
}
106+
}
107+
108+
let value = AtomicU8::new(0);
109+
{
110+
let mut data: [MaybeUninit<DropCounter<'_>>; 1] = [MaybeUninit::uninit()];
111+
Cursor::new(&mut data).finish([DropCounter(&value)]);
112+
}
113+
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 0);
114+
115+
let value = AtomicU8::new(0);
116+
{
117+
let mut data: [MaybeUninit<DropCounter<'_>>; 2] =
118+
[MaybeUninit::uninit(), MaybeUninit::uninit()];
119+
Cursor::new(&mut data).finish([DropCounter(&value), DropCounter(&value)]);
120+
}
121+
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 0);
122+
123+
let value = AtomicU8::new(0);
124+
{
125+
let mut data: [MaybeUninit<DropCounter<'_>>; 1] = [MaybeUninit::uninit()];
126+
Cursor::new(&mut data).finish([DropCounter(&value)]);
127+
let [value] = data;
128+
unsafe { value.assume_init() };
129+
}
130+
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 1);
131+
132+
let value = AtomicU8::new(0);
133+
{
134+
let mut data: [MaybeUninit<DropCounter<'_>>; 2] =
135+
[MaybeUninit::uninit(), MaybeUninit::uninit()];
136+
Cursor::new(&mut data).finish([DropCounter(&value), DropCounter(&value)]);
137+
let [value0, value1] = data;
138+
unsafe { value0.assume_init() };
139+
unsafe { value1.assume_init() };
140+
}
141+
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 2);
142+
}
143+
144+
#[test]
145+
fn test_initalized() {
146+
let mut data: [MaybeUninit<u8>; 4] = [MaybeUninit::new(0); 4];
147+
Cursor::new(&mut data).write([1, 2]).finish([3, 4]);
148+
assert_eq!(data.map(|d| unsafe { d.assume_init() }), [1, 2, 3, 4]);
149+
}
150+
}

crates/planus/src/union_vectors/iterators.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use core::num::NonZeroUsize;
22

3-
use crate::VectorReadUnion;
4-
53
use super::UnionVector;
4+
use crate::VectorReadUnion;
65

76
fn div_ceil(lhs: usize, rhs: usize) -> usize {
87
let d = lhs / rhs;

0 commit comments

Comments
 (0)