Skip to content

Commit 2f698df

Browse files
authored
Merge pull request #90 from RustyYato/ptr-union-clone-fix
Fix UnionN::clone to check alignment of the cloned pointer
2 parents 26c260e + 6cb91b5 commit 2f698df

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

crates/ptr-union/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,9 +558,14 @@ macro_rules! impl_union {
558558
{
559559
paste::paste! {
560560
fn clone(&self) -> Self {
561-
let builder = unsafe { <$Builder<$($A,)*>>::new_unchecked() };
561+
#[cold]
562+
#[inline(never)]
563+
fn clone_error<A>() -> ! {
564+
panic!("Tried to clone {} in a {}, but the cloned pointer wasn't sufficiently aligned", core::any::type_name::<A>(), stringify!($Union))
565+
}
566+
562567
None
563-
$(.or_else(|| self.[<clone_ $a>]().map(|this| builder.$a(this))))*
568+
$(.or_else(|| self.[<clone_ $a>]().map(|this| Self::[<new_ $a>](this).unwrap_or_else(|_| clone_error::<$A>()))))*
564569
.unwrap_or_else(|| unsafe { unreachable_unchecked() })
565570
}
566571
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! This is a regression test for https://github.com/CAD97/pointer-utils/issues/89
2+
//!
3+
//! The idea here is to have a Box like pointer which we can control the alignment for to
4+
//! ensure that the tests are stable (doesn't depend on allocator shenanigans).
5+
6+
use std::{ptr::NonNull, sync::atomic::AtomicUsize};
7+
8+
use ptr_union::Union8;
9+
10+
#[derive(Debug)]
11+
struct MyBox {
12+
ptr: NonNull<u8>,
13+
}
14+
15+
// SAFETY:
16+
// * MyBox doesn't have any shared mutability
17+
// * the address of the returned pointer doesn't depend on the address of MyBox
18+
// * MyBox doesn't implement Deref
19+
unsafe impl erasable::ErasablePtr for MyBox {
20+
fn erase(this: Self) -> erasable::ErasedPtr {
21+
this.ptr.cast()
22+
}
23+
24+
unsafe fn unerase(this: erasable::ErasedPtr) -> Self {
25+
Self { ptr: this.cast() }
26+
}
27+
}
28+
29+
static OFFSET: AtomicUsize = AtomicUsize::new(8);
30+
31+
impl MyBox {
32+
fn new() -> Self {
33+
let offset = OFFSET.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
34+
MyBox {
35+
ptr: NonNull::new(offset as _).unwrap(),
36+
}
37+
}
38+
}
39+
40+
impl Clone for MyBox {
41+
fn clone(&self) -> Self {
42+
Self::new()
43+
}
44+
}
45+
46+
type Union = Union8<
47+
MyBox,
48+
NonNull<u8>,
49+
NonNull<u8>,
50+
NonNull<u8>,
51+
NonNull<u8>,
52+
NonNull<u8>,
53+
NonNull<u8>,
54+
NonNull<u8>,
55+
>;
56+
57+
#[test]
58+
#[allow(clippy::redundant_clone)]
59+
#[should_panic = "but the cloned pointer wasn't sufficiently aligned"]
60+
fn test_clone_unaligned() {
61+
let bx = MyBox::new();
62+
// this can't fail since the first `MyBox` is created at address 8, which is aligned to 8 bytes
63+
let x = Union::new_a(bx).unwrap();
64+
65+
// this clone should panic, since the next `MyBox` is created at address 9, which is not aligned to 8 bytes
66+
let _y = x.clone();
67+
}

0 commit comments

Comments
 (0)