From 9de204a4972c8ff4b2e52cba9aa2476972ff34e6 Mon Sep 17 00:00:00 2001 From: "morgane.baizeau" Date: Tue, 2 Jun 2026 22:28:11 +0200 Subject: [PATCH 1/2] fix aliasing UB, layout UB, and incorrect initialization UB --- src/data/graph.rs | 10 ++++----- .../joint/multibody_joint/multibody.rs | 5 +++++ src/dynamics/rigid_body_components.rs | 21 +++++++++++++++++++ src/dynamics/solver/mod.rs | 9 ++------ src/utils/index_mut2.rs | 17 ++++----------- 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/data/graph.rs b/src/data/graph.rs index dbebb2631..d42cb07de 100644 --- a/src/data/graph.rs +++ b/src/data/graph.rs @@ -146,12 +146,10 @@ fn index_twice(arr: &mut [T], a: usize, b: usize) -> Pair<&mut T> { } else if a == b { Pair::One(&mut arr[max(a, b)]) } else { - // safe because a, b are in bounds and distinct - unsafe { - let ar = &mut *(arr.get_unchecked_mut(a) as *mut _); - let br = &mut *(arr.get_unchecked_mut(b) as *mut _); - Pair::Both(ar, br) - } + // can't panic because a, b are in bounds and distinct + let [ar,br] = arr.get_disjoint_mut([a,b]).unwrap(); + Pair::Both(ar, br) + } } diff --git a/src/dynamics/joint/multibody_joint/multibody.rs b/src/dynamics/joint/multibody_joint/multibody.rs index da3a097fe..3b2c7fb69 100644 --- a/src/dynamics/joint/multibody_joint/multibody.rs +++ b/src/dynamics/joint/multibody_joint/multibody.rs @@ -30,6 +30,11 @@ impl Force { } fn as_vector(&self) -> &SVector { + // SAFETY : this is safe because : + // - Force is repr(C) + // - Vector is repr(C) + // - AngVector is repr(C) or Real + // - total size in Reals is SPATIAL_DIM unsafe { core::mem::transmute(self) } } } diff --git a/src/dynamics/rigid_body_components.rs b/src/dynamics/rigid_body_components.rs index 439a784ba..68545d692 100644 --- a/src/dynamics/rigid_body_components.rs +++ b/src/dynamics/rigid_body_components.rs @@ -504,6 +504,7 @@ impl RigidBodyMassProps { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy, PartialEq)] +#[repr(C)] /// The velocities of this rigid-body. pub struct RigidBodyVelocity { /// The linear velocity of the rigid-body. @@ -593,6 +594,11 @@ impl RigidBodyVelocity { #[inline] #[cfg(feature = "dim2")] pub fn as_vector(&self) -> &na::Vector3 { + // SAFETY : this is safe because : + // - RigidBodyVelocity is repr(C) + // - the vector types used are repr(C) + // - the only non vector type is Real + // - total size in Reals is 3 unsafe { core::mem::transmute(self) } } @@ -602,6 +608,11 @@ impl RigidBodyVelocity { #[inline] #[cfg(feature = "dim2")] pub fn as_vector_mut(&mut self) -> &mut na::Vector3 { + // SAFETY : this is safe because : + // - RigidBodyVelocity is repr(C) + // - the vector types used are repr(C) + // - the only non vector type is Real + // - total size in Reals is 3 unsafe { core::mem::transmute(self) } } @@ -611,6 +622,11 @@ impl RigidBodyVelocity { #[inline] #[cfg(feature = "dim3")] pub fn as_vector(&self) -> &na::Vector6 { + // SAFETY : this is safe because : + // - RigidBodyVelocity is repr(C) + // - the vector types used are repr(C) + // - the only non vector type is Real + // - total size in Reals is 6 unsafe { core::mem::transmute(self) } } @@ -620,6 +636,11 @@ impl RigidBodyVelocity { #[inline] #[cfg(feature = "dim3")] pub fn as_vector_mut(&mut self) -> &mut na::Vector6 { + // SAFETY : this is safe because : + // - RigidBodyVelocity is repr(C) + // - the vector types used are repr(C) + // - the only non vector type is Real + // - total size in Reals is 6 unsafe { core::mem::transmute(self) } } diff --git a/src/dynamics/solver/mod.rs b/src/dynamics/solver/mod.rs index 5a3ffbad4..d907c63be 100644 --- a/src/dynamics/solver/mod.rs +++ b/src/dynamics/solver/mod.rs @@ -34,12 +34,7 @@ mod velocity_solver; // TODO: SAFETY: restrict with bytemuck::Zeroable to make this safe. pub unsafe fn reset_buffer(buffer: &mut Vec, len: usize) { - buffer.clear(); - buffer.reserve(len); + buffer.clear(); - unsafe { - // NOTE: writing zeros is faster than u8::MAX. - buffer.as_mut_ptr().write_bytes(0, len); - buffer.set_len(len); - } + buffer.resize_with(len, || unsafe { core::mem::zeroed()}) } diff --git a/src/utils/index_mut2.rs b/src/utils/index_mut2.rs index 6d81dbb82..9d467bd46 100644 --- a/src/utils/index_mut2.rs +++ b/src/utils/index_mut2.rs @@ -25,14 +25,7 @@ pub trait IndexMut2: IndexMut { impl IndexMut2 for Vec { #[inline] fn index_mut2(&mut self, i: usize, j: usize) -> (&mut T, &mut T) { - assert!(i != j, "Unable to index the same element twice."); - assert!(i < self.len() && j < self.len(), "Index out of bounds."); - - unsafe { - let a = &mut *(self.get_unchecked_mut(i) as *mut _); - let b = &mut *(self.get_unchecked_mut(j) as *mut _); - (a, b) - } + <[T]>::index_mut2(self, i,j) } } @@ -42,10 +35,8 @@ impl IndexMut2 for [T] { assert!(i != j, "Unable to index the same element twice."); assert!(i < self.len() && j < self.len(), "Index out of bounds."); - unsafe { - let a = &mut *(self.get_unchecked_mut(i) as *mut _); - let b = &mut *(self.get_unchecked_mut(j) as *mut _); - (a, b) - } + // cannot panic, per the above checks + let [a,b] = self.get_disjoint_mut([i,j]).unwrap(); + (a,b) } } From c250f8b9eaf7b1198b01a38a84c1d08ff900efa1 Mon Sep 17 00:00:00 2001 From: "morgane.baizeau" Date: Sat, 6 Jun 2026 10:53:23 +0200 Subject: [PATCH 2/2] fix formatting --- src/data/graph.rs | 3 +-- src/dynamics/solver/mod.rs | 4 ++-- src/utils/index_mut2.rs | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/data/graph.rs b/src/data/graph.rs index d42cb07de..b3e1ff7af 100644 --- a/src/data/graph.rs +++ b/src/data/graph.rs @@ -147,9 +147,8 @@ fn index_twice(arr: &mut [T], a: usize, b: usize) -> Pair<&mut T> { Pair::One(&mut arr[max(a, b)]) } else { // can't panic because a, b are in bounds and distinct - let [ar,br] = arr.get_disjoint_mut([a,b]).unwrap(); + let [ar, br] = arr.get_disjoint_mut([a, b]).unwrap(); Pair::Both(ar, br) - } } diff --git a/src/dynamics/solver/mod.rs b/src/dynamics/solver/mod.rs index d907c63be..daa27ec82 100644 --- a/src/dynamics/solver/mod.rs +++ b/src/dynamics/solver/mod.rs @@ -34,7 +34,7 @@ mod velocity_solver; // TODO: SAFETY: restrict with bytemuck::Zeroable to make this safe. pub unsafe fn reset_buffer(buffer: &mut Vec, len: usize) { - buffer.clear(); + buffer.clear(); - buffer.resize_with(len, || unsafe { core::mem::zeroed()}) + buffer.resize_with(len, || unsafe { core::mem::zeroed() }) } diff --git a/src/utils/index_mut2.rs b/src/utils/index_mut2.rs index 9d467bd46..e92974a78 100644 --- a/src/utils/index_mut2.rs +++ b/src/utils/index_mut2.rs @@ -25,7 +25,7 @@ pub trait IndexMut2: IndexMut { impl IndexMut2 for Vec { #[inline] fn index_mut2(&mut self, i: usize, j: usize) -> (&mut T, &mut T) { - <[T]>::index_mut2(self, i,j) + <[T]>::index_mut2(self, i, j) } } @@ -36,7 +36,7 @@ impl IndexMut2 for [T] { assert!(i < self.len() && j < self.len(), "Index out of bounds."); // cannot panic, per the above checks - let [a,b] = self.get_disjoint_mut([i,j]).unwrap(); - (a,b) + let [a, b] = self.get_disjoint_mut([i, j]).unwrap(); + (a, b) } }