Skip to content

Commit

Permalink
glib: Do not use ptr::offset/offset_from for private/impl offset
Browse files Browse the repository at this point in the history
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 <sebastian@centricular.com>
  • Loading branch information
heftig and sdroege committed Jun 6, 2023
1 parent aa4b78e commit dfa87bf
Showing 1 changed file with 68 additions and 31 deletions.
99 changes: 68 additions & 31 deletions glib/src/subclass/types.rs
Expand Up @@ -88,16 +88,59 @@ pub unsafe trait InstanceStructExt: InstanceStruct {
fn class(&self) -> &<Self::Type as ObjectSubclass>::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<T, U>(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::<U>() - 1), 0);
let ptr = ptr as *const U;
ptr
}

// 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<T, U>(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::<U>() - 1), 0);
let ptr = ptr as *mut U;
ptr
}

unsafe impl<T: InstanceStruct> 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::<T, Self::Type>(self, private_offset);
&*imp
}
}
Expand Down Expand Up @@ -717,17 +760,15 @@ impl<T: ObjectSubclass> 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 <Self::Type as ObjectType>::GlibType;
let ptr =
offset_ptr_by_bytes::<Self, <Self::Type as ObjectType>::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))
}
}

Expand All @@ -752,10 +793,7 @@ impl<T: ObjectSubclass> 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<Self>;
let ptr = offset_ptr_by_bytes::<Self, PrivateStruct<Self>>(self, offset);
let priv_ = &*ptr;

match priv_.instance_data {
Expand Down Expand Up @@ -821,9 +859,10 @@ impl<T: ObjectSubclass> InitializingObject<T> {

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<T>;
let ptr = offset_ptr_by_bytes_mut::<
<<T as ObjectSubclass>::Type as ObjectType>::GlibType,
PrivateStruct<T>,
>(self.0.as_ptr(), offset);
let priv_ = &mut *ptr;

if priv_.instance_data.is_none() {
Expand Down Expand Up @@ -885,8 +924,10 @@ unsafe extern "C" fn instance_init<T: ObjectSubclass>(
// 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::<gobject_ffi::GTypeInstance, PrivateStruct<T>>(
obj,
private_offset,
);

assert!(
priv_ptr as usize & (mem::align_of::<PrivateStruct<T>>() - 1) == 0,
Expand All @@ -897,13 +938,11 @@ unsafe extern "C" fn instance_init<T: ObjectSubclass>(
2 * mem::size_of::<usize>(),
);

let priv_storage = priv_ptr as *mut PrivateStruct<T>;

let klass = &*(klass as *const T::Class);

let imp = T::with_class(klass);
ptr::write(
priv_storage,
priv_ptr,
PrivateStruct {
imp,
instance_data: None,
Expand All @@ -925,11 +964,10 @@ unsafe extern "C" fn finalize<T: ObjectSubclass>(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<T>);
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::<gobject_ffi::GObject, PrivateStruct<T>>(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);
Expand Down Expand Up @@ -996,11 +1034,10 @@ pub fn register_type<T: ObjectSubclass>() -> 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::<PrivateStruct<T>>::uninit();
let priv_ = mem::MaybeUninit::<PrivateStruct<T>>::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();
Expand Down

0 comments on commit dfa87bf

Please sign in to comment.