From 4e2a5e5121e446353a0e2339eb1345420ce5a826 Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Mon, 5 Jun 2023 14:56:27 +0200 Subject: [PATCH] glib: Do not use ptr::offset/offset_from for private/impl offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doing this violates Rust's [strict provenance rules][1]. Unfortunately, the API for doing this properly ([`ptr::expose_addr`][2]) isn't ready yet. However, we can work around this on stable Rust by casting through `isize` for offset calculations. [1]: https://doc.rust-lang.org/std/ptr/index.html [2]: https://doc.rust-lang.org/std/primitive.pointer.html#method.expose_addr Co-authored-by: Sebastian Dröge --- glib/src/subclass/types.rs | 97 ++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/glib/src/subclass/types.rs b/glib/src/subclass/types.rs index 802bf2c3971a..7e1cdd566e88 100644 --- a/glib/src/subclass/types.rs +++ b/glib/src/subclass/types.rs @@ -88,16 +88,57 @@ pub unsafe trait InstanceStructExt: InstanceStruct { fn class(&self) -> &::Class; } +// rustdoc-stripper-ignore-next +/// Offset `ptr` by `offset` *bytes* and cast the result to `*const U`. +/// +/// The result must be a correctly aligned pointer to a valid value of type `U`. +/// +/// # Panics: +/// +/// This function panics if debug assertions are enabled if adding `offset` causes `ptr` to +/// overflow or if the resulting pointer is not correctly aligned. +#[inline] +fn offset_ptr_by_bytes(ptr: *const T, offset: isize) -> *const U { + // FIXME: Use `ptr::expose_addr()` once stable + let ptr = ptr as usize; + let ptr = if offset < 0 { + ptr - (-offset) as usize + } else { + ptr + offset as usize + }; + debug_assert_eq!(ptr & (mem::align_of::() - 1), 0); + ptr as *const U +} + +// rustdoc-stripper-ignore-next +/// Offset `ptr` by `offset` *bytes* and cast the result to `*mut U`. +/// +/// The result must be a correctly aligned pointer to a valid value of type `U`. +/// +/// # Panics: +/// +/// This function panics if debug assertions are enabled if adding `offset` causes `ptr` to +/// overflow or if the resulting pointer is not correctly aligned. +#[inline] +fn offset_ptr_by_bytes_mut(ptr: *mut T, offset: isize) -> *mut U { + // FIXME: Use `ptr::expose_addr()` once stable + let ptr = ptr as usize; + let ptr = if offset < 0 { + ptr - (-offset) as usize + } else { + ptr + offset as usize + }; + debug_assert_eq!(ptr & (mem::align_of::() - 1), 0); + ptr as *mut U +} + unsafe impl InstanceStructExt for T { #[inline] fn imp(&self) -> &Self::Type { unsafe { let data = Self::Type::type_data(); let private_offset = data.as_ref().impl_offset(); - let ptr: *const u8 = self as *const _ as *const u8; - let imp_ptr = ptr.offset(private_offset); - let imp = imp_ptr as *const Self::Type; - + let imp = offset_ptr_by_bytes::(self, private_offset); &*imp } } @@ -717,17 +758,15 @@ impl ObjectSubclassExt for T { debug_assert!(type_.is_valid()); let offset = -data.as_ref().impl_offset(); - - let ptr = self as *const Self as *const u8; - let ptr = ptr.offset(offset); - let ptr = ptr as *mut u8 as *mut ::GlibType; + let ptr = + offset_ptr_by_bytes::::GlibType>(self, offset); // The object might just be finalized, and in that case it's unsafe to access // it and use any API on it. This can only happen from inside the Drop impl // of Self. - debug_assert_ne!((*(ptr as *mut gobject_ffi::GObject)).ref_count, 0); + debug_assert_ne!((*(ptr as *const gobject_ffi::GObject)).ref_count, 0); - crate::BorrowedObject::new(ptr) + crate::BorrowedObject::new(mut_override(ptr)) } } @@ -752,10 +791,7 @@ impl ObjectSubclassExt for T { debug_assert!(self_type_.is_valid()); let offset = -type_data.as_ref().private_imp_offset; - - let ptr = self as *const Self as *const u8; - let ptr = ptr.offset(offset); - let ptr = ptr as *const PrivateStruct; + let ptr = offset_ptr_by_bytes::>(self, offset); let priv_ = &*ptr; match priv_.instance_data { @@ -821,9 +857,10 @@ impl InitializingObject { let offset = type_data.as_ref().private_offset; - let ptr = self.0.as_ptr() as *mut u8; - let ptr = ptr.offset(offset); - let ptr = ptr as *mut PrivateStruct; + let ptr = offset_ptr_by_bytes_mut::< + <::Type as ObjectType>::GlibType, + PrivateStruct, + >(self.0.as_ptr(), offset); let priv_ = &mut *ptr; if priv_.instance_data.is_none() { @@ -885,8 +922,10 @@ unsafe extern "C" fn instance_init( // and actually store it in that place. let mut data = T::type_data(); let private_offset = data.as_mut().private_offset; - let ptr = obj as *mut u8; - let priv_ptr = ptr.offset(private_offset); + let priv_ptr = offset_ptr_by_bytes_mut::>( + obj, + private_offset, + ); assert!( priv_ptr as usize & (mem::align_of::>() - 1) == 0, @@ -897,13 +936,11 @@ unsafe extern "C" fn instance_init( 2 * mem::size_of::(), ); - let priv_storage = priv_ptr as *mut PrivateStruct; - let klass = &*(klass as *const T::Class); let imp = T::with_class(klass); ptr::write( - priv_storage, + priv_ptr, PrivateStruct { imp, instance_data: None, @@ -925,11 +962,10 @@ unsafe extern "C" fn finalize(obj: *mut gobject_ffi::GObject) // Retrieve the private struct and drop it for freeing all associated memory. let mut data = T::type_data(); let private_offset = data.as_mut().private_offset; - let ptr = obj as *mut u8; - let priv_ptr = ptr.offset(private_offset); - let priv_storage = &mut *(priv_ptr as *mut PrivateStruct); - ptr::drop_in_place(&mut priv_storage.imp); - ptr::drop_in_place(&mut priv_storage.instance_data); + let priv_ptr = + offset_ptr_by_bytes_mut::>(obj, private_offset); + ptr::drop_in_place(ptr::addr_of_mut!((*priv_ptr).imp)); + ptr::drop_in_place(ptr::addr_of_mut!((*priv_ptr).instance_data)); // Chain up to the parent class' finalize implementation, if any. let parent_class = &*(data.as_ref().parent_class() as *const gobject_ffi::GObjectClass); @@ -996,11 +1032,10 @@ pub fn register_type() -> Type { // some hoops because Rust doesn't have an offsetof operator yet. data.as_mut().private_imp_offset = { // Must not be a dangling pointer so let's create some uninitialized memory - let priv_ = std::mem::MaybeUninit::>::uninit(); + let priv_ = mem::MaybeUninit::>::uninit(); let ptr = priv_.as_ptr(); - let imp_ptr = std::ptr::addr_of!((*ptr).imp) as *const u8; - let ptr = ptr as *const u8; - imp_ptr.offset_from(ptr) + let imp_ptr = ptr::addr_of!((*ptr).imp); + (imp_ptr as isize) - (ptr as isize) }; let iface_types = T::Interfaces::iface_infos();